GUI reslist: fixed webengine image display issue, but failed with reliably getting scroll position for paging (cf QTBUG-105842)

This commit is contained in:
Jean-Francois Dockes 2022-08-20 12:29:06 +02:00
parent 426fee8a86
commit 8bd38a858d
2 changed files with 110 additions and 56 deletions

View File

@ -149,7 +149,7 @@ public:
// Now also set for webkit because, as we set baseURL to file://, // Now also set for webkit because, as we set baseURL to file://,
// the relative links we set in the list will also be prefixed (by // the relative links we set in the list will also be prefixed (by
// the HTML engine) // the HTML engine)
virtual string linkPrefix() override {return "file:///";} virtual string linkPrefix() override {return "file:///recoll-links/";}
virtual string bodyAttrs() override { virtual string bodyAttrs() override {
return "onload=\"addEventListener('contextmenu', saveLoc)\""; return "onload=\"addEventListener('contextmenu', saveLoc)\"";
} }
@ -305,17 +305,21 @@ ResList::ResList(QWidget* parent, const char* name)
#if defined(USING_WEBKIT) || defined(USING_WEBENGINE) #if defined(USING_WEBKIT) || defined(USING_WEBENGINE)
setPage(new RclWebPage(this)); setPage(new RclWebPage(this));
settings()->setAttribute(QWEBSETTINGS::JavascriptEnabled, true);
#ifdef USING_WEBKIT #ifdef USING_WEBKIT
LOGDEB("Reslist: using Webkit\n"); LOGDEB("Reslist: using Webkit\n");
page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
// signals and slots connections // signals and slots connections
connect(this, SIGNAL(linkClicked(const QUrl &)), connect(this, SIGNAL(linkClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &)));
this, SLOT(onLinkClicked(const QUrl &)));
#else #else
LOGDEB("Reslist: using Webengine\n"); LOGDEB("Reslist: using Webengine\n");
connect(page(), SIGNAL(loadFinished(bool)), this, SLOT(runStoredJS(bool))); connect(page(), SIGNAL(loadFinished(bool)), this, SLOT(runStoredJS(bool)));
// These appear to get randomly disconnected or never connected.
connect(page(), SIGNAL(scrollPositionChanged(const QPointF &)),
this, SLOT(onPageScrollPositionChanged(const QPointF &)));
connect(page(), SIGNAL(contentsSizeChanged(const QSizeF &)),
this, SLOT(onPageContentsSizeChanged(const QSizeF &)));
#endif #endif
settings()->setAttribute(QWEBSETTINGS::JavascriptEnabled, true);
#else #else
LOGDEB("Reslist: using QTextBrowser\n"); LOGDEB("Reslist: using QTextBrowser\n");
setReadOnly(true); setReadOnly(true);
@ -323,8 +327,7 @@ ResList::ResList(QWidget* parent, const char* name)
setOpenLinks(false); setOpenLinks(false);
setTabChangesFocus(true); setTabChangesFocus(true);
// signals and slots connections // signals and slots connections
connect(this, SIGNAL(anchorClicked(const QUrl &)), connect(this, SIGNAL(anchorClicked(const QUrl &)), this, SLOT(onLinkClicked(const QUrl &)));
this, SLOT(onLinkClicked(const QUrl &)));
#endif #endif
setFont(); setFont();
@ -388,6 +391,20 @@ void ResList::setRclMain(RclMain *m, bool ismain)
} }
} }
#if defined(USING_WEBKIT) || defined(USING_WEBENGINE)
void ResList::runJS(const QString& js)
{
LOGDEB("runJS: " << qs2utf8s(js) << "\n");
#if defined(USING_WEBKIT)
page()->mainFrame()->evaluateJavaScript(js);
#elif defined(USING_WEBENGINE)
page()->runJavaScript(js);
// page()->runJavaScript(js, [](const QVariant &v) {qDebug() << v.toString();});
#endif
}
#if defined(USING_WEBENGINE)
void ResList::runStoredJS(bool res) void ResList::runStoredJS(bool res)
{ {
if (m_js.isEmpty()) { if (m_js.isEmpty()) {
@ -403,17 +420,39 @@ void ResList::runStoredJS(bool res)
m_js.clear(); m_js.clear();
} }
void ResList::runJS(const QString& js) void ResList::onPageScrollPositionChanged(const QPointF &position)
{ {
#if defined(USING_WEBKIT) LOGDEB0("ResList::onPageScrollPositionChanged: y : " << position.y() << "\n");
page()->mainFrame()->evaluateJavaScript(js); m_scrollpos = position;
#elif defined(USING_WEBENGINE)
page()->runJavaScript(js);
#else
Q_UNUSED(js);
#endif
} }
void ResList::onPageContentsSizeChanged(const QSizeF &size)
{
LOGDEB0("ResList::onPageContentsSizeChanged: y : " << size.height() << "\n");
m_contentsize = size;
}
#endif // WEBENGINE
static void maybeDump(const QString& text)
{
std::string dumpfile;
if (!theconfig->getConfParam("reslisthtmldumpfile", dumpfile) || dumpfile.empty()) {
return;
}
dumpfile = path_tildexpand(dumpfile);
if (path_exists(dumpfile)) {
return;
}
auto fp = fopen(dumpfile.c_str(), "w");
if (fp) {
auto s = qs2utf8s(text);
fwrite(s.c_str(), 1, s.size(), fp);
fclose(fp);
}
}
#endif // WEBKIT or WEBENGINE
void ResList::onUiPrefsChanged() void ResList::onUiPrefsChanged()
{ {
setFont(); setFont();
@ -454,7 +493,7 @@ void ResList::setDocSource(std::shared_ptr<DocSequence> nsource)
void ResList::readDocSource() void ResList::readDocSource()
{ {
LOGDEB("ResList::readDocSource()\n"); LOGDEB("ResList::readDocSource()\n");
resetView(); m_curPvDoc = -1;
if (!m_source) if (!m_source)
return; return;
m_listId = newListId(); m_listId = newListId();
@ -648,9 +687,9 @@ void ResList::resPageUpOrBack()
setupArrows(); setupArrows();
#elif defined(USING_WEBENGINE) #elif defined(USING_WEBENGINE)
if (scrollIsAtTop()) { if (scrollIsAtTop()) {
// Displaypage first calls resetview() which causes a page load event. We want to run the js // Displaypage used to call resetview() which caused a page load event. We wanted to run the
// on the second event. // js on the second event, with countdown = 1. Not needed any more, but kept around.
m_js_countdown = 1; m_js_countdown = 0;
m_js = "window.scrollBy(0,50000);"; m_js = "window.scrollBy(0,50000);";
resultPageBack(); resultPageBack();
} else { } else {
@ -677,8 +716,10 @@ void ResList::resPageDownOrNext()
setupArrows(); setupArrows();
#elif defined(USING_WEBENGINE) #elif defined(USING_WEBENGINE)
if (scrollIsAtBottom()) { if (scrollIsAtBottom()) {
LOGDEB0("downOrNext: at bottom: call resultPageNext\n");
resultPageNext(); resultPageNext();
} else { } else {
LOGDEB0("downOrNext: scroll\n");
QString js = QString("window.scrollBy(%1, %2);").arg(0).arg(int(0.9*geometry().height())); QString js = QString("window.scrollBy(%1, %2);").arg(0).arg(int(0.9*geometry().height()));
runJS(js); runJS(js);
} }
@ -693,12 +734,6 @@ void ResList::resPageDownOrNext()
#endif #endif
} }
void ResList::setupArrows()
{
emit prevPageAvailable(m_pager->hasPrev() || !scrollIsAtTop());
emit nextPageAvailable(m_pager->hasNext() || !scrollIsAtBottom());
}
bool ResList::scrollIsAtBottom() bool ResList::scrollIsAtBottom()
{ {
#if defined(USING_WEBKIT) #if defined(USING_WEBKIT)
@ -715,10 +750,20 @@ bool ResList::scrollIsAtBottom()
LOGDEB2("scrollIsAtBottom: returning " << ret << "\n"); LOGDEB2("scrollIsAtBottom: returning " << ret << "\n");
return ret; return ret;
#elif defined(USING_WEBENGINE) #elif defined(USING_WEBENGINE)
QSize css = page()->contentsSize().toSize(); // TLDR: Could not find any way whatsoever to reliably get the page contents size or position
// with either signals or direct calls. No obvious way to get this from javascript either. This
// used to work, with direct calls and broke down around qt 5.15
QSize wss = size(); QSize wss = size();
QPoint sp = page()->scrollPosition().toPoint();
LOGDEB1("atBottom: contents W " << css.width() << " H " << css.height() << //QSize css = page()->contentsSize().toSize();
QSize css = m_contentsize.toSize();
// Does not work with recent (2022) qt releases: always 0
//auto spf = page()->scrollPosition();
// Found no easy way to block waiting for the js output
//runJS("document.body.scrollTop", [](const QVariant &v) {qDebug() << v.toInt();});
QPoint sp = m_scrollpos.toPoint();
LOGDEB0("atBottom: contents W " << css.width() << " H " << css.height() <<
" widget W " << wss.width() << " Y " << wss.height() << " widget W " << wss.width() << " Y " << wss.height() <<
" scroll X " << sp.x() << " Y " << sp.y() << "\n"); " scroll X " << sp.x() << " Y " << sp.y() << "\n");
// This seems to work but it's mysterious as points and pixels // This seems to work but it's mysterious as points and pixels
@ -729,6 +774,7 @@ bool ResList::scrollIsAtBottom()
#endif #endif
} }
bool ResList::scrollIsAtTop() bool ResList::scrollIsAtTop()
{ {
#if defined(USING_WEBKIT) #if defined(USING_WEBKIT)
@ -745,12 +791,21 @@ bool ResList::scrollIsAtTop()
LOGDEB2("scrollIsAtTop: returning " << ret << "\n"); LOGDEB2("scrollIsAtTop: returning " << ret << "\n");
return ret; return ret;
#elif defined(USING_WEBENGINE) #elif defined(USING_WEBENGINE)
return page()->scrollPosition().toPoint().ry() == 0; //return page()->scrollPosition().toPoint().ry() == 0;
return m_scrollpos.y() == 0;
#else #else
return false; return false;
#endif #endif
} }
void ResList::setupArrows()
{
emit prevPageAvailable(m_pager->hasPrev() || !scrollIsAtTop());
emit nextPageAvailable(m_pager->hasNext() || !scrollIsAtBottom());
}
// Show previous page of results. We just set the current number back // Show previous page of results. We just set the current number back
// 2 pages and show next page. // 2 pages and show next page.
void ResList::resultPageBack() void ResList::resultPageBack()
@ -807,35 +862,18 @@ void ResList::append(const QString &text)
#endif #endif
} }
static void maybeDump(const QString& text)
{
std::string dumpfile;
if (!theconfig->getConfParam("reslisthtmldumpfile", dumpfile) || dumpfile.empty()) {
return;
}
dumpfile = path_tildexpand(dumpfile);
if (path_exists(dumpfile)) {
return;
}
auto fp = fopen(dumpfile.c_str(), "w");
if (fp) {
auto s = qs2utf8s(text);
fwrite(s.c_str(), 1, s.size(), fp);
fclose(fp);
}
}
void ResList::displayPage() void ResList::displayPage()
{ {
resetView();
#if defined(USING_WEBENGINE) || defined(USING_WEBKIT) #if defined(USING_WEBENGINE) || defined(USING_WEBKIT)
const static QUrl baseUrl("file:///");
QProgressDialog progress("Generating text snippets...", "", 0, prefs.respagesize, this); QProgressDialog progress("Generating text snippets...", "", 0, prefs.respagesize, this);
m_residx = 0; m_residx = 0;
progress.setWindowModality(Qt::WindowModal); progress.setWindowModality(Qt::WindowModal);
progress.setCancelButton(nullptr); progress.setCancelButton(nullptr);
progress.setMinimumDuration(2000); progress.setMinimumDuration(2000);
m_progress = &progress; m_progress = &progress;
m_text = "";
#endif #endif
m_pager->displayPage(theconfig); m_pager->displayPage(theconfig);
@ -847,9 +885,11 @@ void ResList::displayPage()
m_progress->close(); m_progress->close();
m_progress = nullptr; m_progress = nullptr;
} }
const static QUrl baseUrl("file:///"); if (m_lasttext == m_text)
return;
maybeDump(m_text); maybeDump(m_text);
setHtml(m_text, baseUrl); setHtml(m_text, baseUrl);
m_lasttext = m_text;
#endif #endif
LOGDEB0("ResList::displayPg: hasNext " << m_pager->hasNext() << LOGDEB0("ResList::displayPg: hasNext " << m_pager->hasNext() <<
@ -867,7 +907,7 @@ void ResList::previewExposed(int docnum)
LOGDEB("ResList::previewExposed: doc " << docnum << "\n"); LOGDEB("ResList::previewExposed: doc " << docnum << "\n");
// Possibly erase old one to white // Possibly erase old one to white
if (m_curPvDoc != -1) { if (m_curPvDoc > -1) {
#if defined(USING_WEBKIT) #if defined(USING_WEBKIT)
QString sel = QString sel =
QString("div[rcldocnum=\"%1\"]").arg(m_curPvDoc - pageFirstDocNum()); QString("div[rcldocnum=\"%1\"]").arg(m_curPvDoc - pageFirstDocNum());
@ -901,12 +941,14 @@ void ResList::previewExposed(int docnum)
m_curPvDoc = -1; m_curPvDoc = -1;
} }
// Set background for active preview's doc entry if ((m_curPvDoc = docnum) < 0) {
m_curPvDoc = docnum; return;
}
// Set background for active preview's doc entry
#if defined(USING_WEBKIT) #if defined(USING_WEBKIT)
QString sel = QString sel = QString("div[rcldocnum=\"%1\"]").arg(docnum - pageFirstDocNum());
QString("div[rcldocnum=\"%1\"]").arg(docnum - pageFirstDocNum());
LOGDEB2("Searching for element, selector: [" << qs2utf8s(sel) << "]\n"); LOGDEB2("Searching for element, selector: [" << qs2utf8s(sel) << "]\n");
QWebElement elt = page()->mainFrame()->findFirstElement(sel); QWebElement elt = page()->mainFrame()->findFirstElement(sel);
if (!elt.isNull()) { if (!elt.isNull()) {

View File

@ -124,11 +124,15 @@ protected:
public slots: public slots:
virtual void onLinkClicked(const QUrl &); virtual void onLinkClicked(const QUrl &);
virtual void onPopupJsDone(const QVariant&); virtual void onPopupJsDone(const QVariant&);
void runJS(const QString& js);
protected slots: protected slots:
virtual void languageChange(); virtual void languageChange();
void runStoredJS(bool);
void setupArrows(); void setupArrows();
#if defined(USING_WEBENGINE)
void runStoredJS(bool);
void onPageScrollPositionChanged(const QPointF &position);
void onPageContentsSizeChanged(const QSizeF &size);
#endif // USING_WEBENGINE
private: private:
QtGuiResListPager *m_pager{0}; QtGuiResListPager *m_pager{0};
@ -144,6 +148,14 @@ private:
QString m_text; QString m_text;
QProgressDialog *m_progress{nullptr}; QProgressDialog *m_progress{nullptr};
int m_residx{0}; // result index in page int m_residx{0}; // result index in page
void runJS(const QString& js);
#if defined(USING_WEBENGINE)
// Webengine local image display appears to break randomly (for some versions and platforms,
// circa 2022) when we display the same data multiple times. Detect and avoid.
QString m_lasttext;
QPointF m_scrollpos{0,0};
QSizeF m_contentsize{0,0};
#endif // WEBENGINE
#else #else
// Translate from textedit paragraph number to relative // Translate from textedit paragraph number to relative
// docnum. Built while we insert text into the qtextedit // docnum. Built while we insert text into the qtextedit