Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QtWebView - How to enable scrolling of page and scrolling of elements in a page (e.g. Google Maps)

I've run into a bit of an issue related to a whitelist Web Browser my company has been developing / maintaining for one of our product lines. The browser runs on top of Qt 4.8.6, using qtwebkit (Migration to 5.X would be ideal, but the embedded Linux OS we're using is too old to support the newer versions based on our testing, and upgrading to a newer OS is too costly to us / our customers). The primary interface to the browser is a 6x8 touchscreen, mounted inside an aircraft cockpit.

For sites that have things like scrollable/embedded maps (ex. Google Maps), the users of the browser want the ability to drag the entire page when they are selecting something outside of the map, and drag just the map (without the entire page scrolling) when the map is selected (Ala most of the popular mobile browsers).

Thus far, I am able to do one or the other, but not both:

  • When I hook mouse handlers into a QWebView or QGraphicsWebView, I can turn the cursor into a hand and very easily support dragging of the entire web page. However, that inhibits the page's ability to handle the mouse events for when a user is pulling over a map (i.e. When a user drags over a map, it drags the entire page without moving the map).

  • When I don't add in the hooks to handle mouse events, things like maps are scrollable by grapping/dragging them, but of course the user loses the ability to drag the entire page.

Right now, the browser uses the later, with scroll bars disabled and a directional-arrow overlay to allow the user to scroll the entire page (as the display size is limited, and scrollbars take up too much space when they are sized large enough for the user to interact with them)...but this is not ideal.

My Question: Is there any easy way to make it so that the page, and elements in a page, can be scrolled seamlessly?

Thanks! Rob

like image 211
Bob Avatar asked Oct 31 '22 11:10

Bob


1 Answers

Seems to me like you need to check if you are over such a map and ignore(pass along) the event in that case. I think you should be able to do something like this:

bool GraphicsWebView::isOverMap(QPoint pos) {
    QWebPage* webPage = this->page();
    if (webPage) {
        QWebFrame* webFrame = webPage->frameAt(pos);
        if (webFrame) {
            QString selectorQuery = "#map-canvas"; // Based on https://developers.google.com/maps/tutorials/fundamentals/adding-a-google-map
            QList<QWebElement> list = webFrame->findAllElements(selectorQuery).toList(); // Find all the maps!

            foreach(QWebElement element, list) {
                if (element.geometry().contains(pos)) {
                    return true; // Cursor is over a map
                }
            }
        }
    }
    return false; // No match
}

Obviously this is a pretty specific function but there is probably a way to come up with a better selector query that will apply to all those kinds of QWebElement.

Assuming you hook mouse events by subclassing QGraphicsWebView and reimplementing void mouseMoveEvent(QGraphicsSceneMouseEvent * event), I suggest you do something like:

void GraphicsWebView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
    if (isOverMap(mapFromScene(event->scenePos()).toPoint())) { // We got a map!
        event.ignore(); // Clear the accept flag
        return; // Return, we're done here
    }

    handleMoveView(); // Not over any maps, let's scroll the page
}

This part of the doc explains how events are handled with regard to the topmost item. I especially recommend you read the third paragraph.

Hope that helps!

EDIT: Did a bit more research and it looks like something like that could be more generic: graphicsView.focusItem()->flags().testFlag(QGraphicsItem::ItemIsMovable);

It's at the very least worth investigating as a replacement to isOverMap()

EDIT: Gotcha, here is something you can try then. Start by subclassing QGraphicsSceneMouseEvent and add a signal called void destroyedWithoutAccept() that's emitted in the destructor if the event has not been accepted.

Then modify mouseMoveEvent to look like this:

void GraphicsWebView::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
    MyEvent myEvent = new MyEvent(event); // Copy event
    event.accept(); // accept original event
    connect(myEvent, SIGNAL(destroyedWithoutAccept),
            this, SLOT(handleMoveView)); // Callback if unused

    QGraphicsWebView::mouseMoveEvent(myEvent); // Pass it to Base class
}

If that works, it might introduce a bit of delay if deleteLater is used to destroy it. But in that case reimplement it as well.

like image 76
Atlante45 Avatar answered Nov 15 '22 03:11

Atlante45