I have an application ported from an old Qt 4.7.4 to Qt5, and as I understood, QWebView became QWebEngineView, and with a QWebView I used FlickCharm, that was in Qt examples, it still works fine with QScrollArea (such as QListWidget, QTableWidget, ... and of course base QScrollArea), but no more with QWebEngineView, here is the code to activate FlickCharm, that was working on Qt4:
void FlickCharm::activateOn(QWidget *widget, QWidget* p_viewport)
{
QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
if (scrollArea) {
// Widget is a scroll area
QAbstractItemView *itemView = qobject_cast<QAbstractItemView*>(widget);
if(itemView)
{
itemView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
itemView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
}
QWidget *viewport = scrollArea->viewport();
if ( p_viewport )
{
viewport = p_viewport;
}
else
{
scrollArea->installEventFilter(this);
}
viewport->installEventFilter(this);
QHash<QWidget*, FlickData*>::iterator oldViewport;
if ( ( oldViewport = d->flickData.find(viewport) ) != d->flickData.end() )
{
delete oldViewport.value();
}
d->flickData.remove(viewport);
d->flickData[viewport] = new FlickData;
d->flickData[viewport]->widget = widget;
d->flickData[viewport]->state = FlickData::Steady;
d->flickData[viewport]->customViewPort = (viewport != scrollArea->viewport());
return;
}
QWebView *webView = qobject_cast<QWebView*>(widget);
if (webView) {
// Widget is a web view
webView->installEventFilter(this);
QHash<QWidget*, FlickData*>::iterator oldViewport;
if ( ( oldViewport = d->flickData.find(webView) ) != d->flickData.end() )
{
delete oldViewport.value();
}
d->flickData.remove(webView);
d->flickData[webView] = new FlickData;
d->flickData[webView]->widget = webView;
d->flickData[webView]->state = FlickData::Steady;
return;
}
qWarning() << "FlickCharm only works on QAbstractScrollArea (and derived classes)";
qWarning() << "or QWebView (and derived classes)";
}
And in FlickData class there is following function that does the scroll:
bool scrollWidget(const int dx, const int dy)
{
{
QAbstractScrollArea *scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
if (scrollArea) {
const int x = scrollArea->horizontalScrollBar()->value();
const int y = scrollArea->verticalScrollBar()->value();
scrollArea->horizontalScrollBar()->setValue(x - dx);
scrollArea->verticalScrollBar()->setValue(y - dy);
return (scrollArea->horizontalScrollBar()->value() != x
|| scrollArea->verticalScrollBar()->value() != y);
}
}
{
QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
if (webEngineView) {
const QPointF position = webEngineView->page()->scrollPosition();
const QPointF newPosition = position - QPointF(dx, dy);
webEngineView->page()->runJavaScript(QString("window.scrollTo(%1, %2);").arg(newPosition.x()).arg(newPosition.y()));
return webEngineView->page()->scrollPosition() != position;
}
}
return false;
}
In Qt5 I tried applying directly to QWebEngineView as above:
QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
if (webEngineView) {
webEngineView->installEventFilter(this);
QHash<QWidget*, FlickData*>::iterator oldViewport;
if ( ( oldViewport = d->flickData.find(webEngineView) ) != d->flickData.end() )
{
delete oldViewport.value();
}
d->flickData.remove(webEngineView);
d->flickData[webEngineView] = new FlickData;
d->flickData[webEngineView]->widget = webEngineView;
d->flickData[webEngineView]->state = FlickData::Steady;
return;
}
And also tried to page view that I assume was viewport:
QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
if (webEngineView) {
QWidget *viewport = webEngineView->page()->view();
webEngineView->installEventFilter(this);
viewport->installEventFilter(this);
QHash<QWidget*, FlickData*>::iterator oldViewport;
if ( ( oldViewport = d->flickData.find(viewport) ) != d->flickData.end() )
{
delete oldViewport.value();
}
d->flickData.remove(viewport);
d->flickData[viewport] = new FlickData;
d->flickData[viewport]->widget = webEngineView;
d->flickData[viewport]->state = FlickData::Steady;
return;
}
In QWebEngineView::page() (that is a QWebEnginePage), there is a scrollPosition() function but this is from a Q_PROPERTY but with no write accessor function, but I found a code peace to scroll with javascript I tried using:
QWebEngineView *webEngineView = qobject_cast<QWebEngineView*>(widget);
if (webEngineView) {
const QPointF position = webEngineView->page()->scrollPosition();
const QPointF newPosition = position - QPointF(dx, dy);
webEngineView->page()->runJavaScript(QString("window.scrollTo(%1, %2);").arg(newPosition.x()).arg(newPosition.y()));
return webEngineView->page()->scrollPosition() != position;
}
But after adding some logs I see I never pass in scrollWidget for a QWebEngineView, and, as I have my own class that inherits QApplication that is instanciated, I can see this is not directly the QWebEngineView that is clicked but a RenderWidgetHostViewQtDelegateWidget (that has QWebEngineView as parent) that is not accessible from anywhere, I see this because I reimplemented MyApplication::notify to log on which widget clicks are made:
bool MyApplication::notify(QObject* p_object, QEvent* p_event)
{
[...]
if ( p_event->type() == QEvent::MouseButtonPress )
{
QWidget* widget = dynamic_cast<QWidget*>(p_object);
if (widget != 0)
{
qDebug().nospace() << "Mouse pressed on [" << (widget->isEnabled() ? "enabled" :"disabled") << "] widget: "
<< p_object
<< ", parent: " << p_object->parent();
}
else
{
qDebug().nospace() << "Mouse pressed on object: " << p_object
<< ", parent: " << p_object->parent();
}
}
else if ( p_event->type() == QEvent::MouseButtonRelease )
{
QWidget* widget = dynamic_cast<QWidget*>(p_object);
if (widget != 0)
{
qDebug().nospace() << "Mouse release on [" << (widget->isEnabled() ? "enabled" :"disabled") << "] widget: "
<< p_object
<< ", parent: " << p_object->parent();
}
else
{
qDebug().nospace() << "Mouse release on object: " << p_object
<< ", parent: " << p_object->parent();
}
}
[...]
}
Also, when I try to scroll on the QWebEngineView, text is selected instead.
Here is the full code of flickcharm: https://doc.qt.io/archives/qt-4.8/qt-demos-embedded-anomaly-src-flickcharm-cpp.html
Also, I've seen there is a solution to make a WebEngineView flickable but only for QtQuick QML: https://stackoverflow.com/a/42817245
Anyone knows how to have touch scrolling on a QWebEngineView ?
Thanks
Edit: since RenderWidgetHostViewQtDelegateWidget is a child of the QWebEngineView, I tried to access it (at least as a QWidget if it was possible) from webEngineView->children() but unsuccessfully since its only children are its QVBoxLayout (that is empty) and the FlickCharm.
I finally managed to do what I want, in FlickCharm::activateOn for QWebEngineView I put:
And
FlickCharm::ReactToWebEngineViewLoaded()
is:With of course
void ReactToWebEngineViewLoaded()
declared as slot in flickcharm.h.And in
FlickData::scrollWidget
for QWebEngineView(this is tricky, and I don't like the qApp->processEvents calls but they are mandatory if we want to actually compare values for return of the function):