There are a few problems left to solve for how Qt’s widgets are embedded inside a QGraphicsScene. Each problem is a bit tricky, at least I think so. I’ll list a few and try to explain.
1) Mapping between global coordinates and scene/item coordinates.
QWidget can map its local coordinates to and from global (desktop) coordinates. This is useful for positioning popups or other windows relative to the widget (since window positions are always defined in global coordinates). It’s also useful for ensuring that something is close to the mouse cursor. There’s basically many use cases for global coordinates, and mapping to and from them. For some event handlers, Qt does this for you automatically, such as QWidget::mousePressEvent().
void Widget::mousePressEvent(QMouseEvent *event)
// or simply menu.exec(event->globalPos());
Now the problem. One vanilla Qt widget can only appear in one spot, one desktop. In Graphics View, it can appear in several places (e.g., in the chip demo). This means QCursor::pos() can mean different things to a widget, and in particular, QWidget::mapToGlobal() and QWidget::mapFromGlobal(). What do these functions really mean to a widget that is embedded inside a QGraphicsProxyWidget? Why does Graphics View support multiple views again? 😛 It’s clear that with only one view, this problem is trivial to solve. What would you expect for the above mouse handler if the widget is embedded in a multi-view application?
It’s likely that when a widget wants to map to and from global coordinates, then it’s as a response to either key or mouse input, and it’s likely that the correct view has input focus. So QWidget::mapToGlobal() could mean: map to the desktop of the single view if there’s only one, or the active input view, if any. If there are several views but none qualify, pick the one closest to the cursor? If not, map to scene. (Yup, you may have two views, each on a different desktop. It’s a corner case, its importance depending on which parts are important in any particular project.) Behind each unhandled corner-case lies a pool of subtle bugs.
QWidget::mapToGlobal() could just mean “map to/from scene”. While consistent, this is also completely useless… QWidget::mapTo/FromGlobal are almost exclusively called from inside a widget that’s otherwise unaware of Graphics View. The result is expected to be in desktop coordinates, and the mapping relative to the widget.
2) To embed or not to embed.
If you embed a widget, its child windows and popups are also embedded. This makes sense sometimes, otherwise not. It’s nice to be able to scale your whole UI to get resolution independence, and also see that your popups and file dialogs are scaled. If you don’t want to scale the windows; just scale the rest of the UI, you can pass 0 for the parent of the window. That’s if you’re in control. If you’re not, widgets like QComboBox will open a popup whose parent is the QComboBox itself, and the popup is embedded automatically. You have no choice. 😛
It’s clear from our users’ feedback, and from the Plasma team in KDE, that sometimes you want this and sometimes you don’t.
So what do we do? We could add a widget attribute that you could set on a widget, that ensures that it never auto-embeds any of its subwindows. In retrospect, perhaps this flag should have been the default, or rather that there was a flag that you could set that prevents this from happening. We also have the option of adding this as an option to QGraphicsProxyWidget; proxy->setAutoEmbedChildWindows(false).
But then there’s the case where a widget just never should be embedded, regardless of its parent. Examples may be tooltips, drag icons, and whatsthis-labels. Should it be the widget’s responsibility to “tag itself” for never being embedded (Qt::WA_DontEmbedInGraphicsView), or is also this an option controlled by the embedder?
I’m sure the answers will pop up one of these days. 😉 Maybe you have some ideas you could share?
(Btw, the fact that embedded popups/windows aren’t constrained to fit inside the viewport is a bug that’s still not closed, but hopefully soon will be.)