4
votes

I'm trying to create a detachable type style widget, like in the way Chrome tabs are detachable (class is called Tab). I have everything working, except for a bug where sometimes (maybe 50% of the time), the Tab object never gets the mouse release event, and stops getting mouse move events.

Essentially, the detaching system works by allowing drags in the mouse press/move/release functions, just like normal. The mouseMoveEvent checks the total distance moved from the start, and if over a certain amount, will start the "detaching" process. The detaching process involves setting the parent widget to 0 (top level widget, undecorated window), so the Tab object is pretty much floating above everything, under the mouse, and continues to be dragged along with it until released.

I ran through all the QEvent items being delivered, and I found that when this issue occurs, the QEvent::MouseMove items (and all mouse events after this) are being sent to the TabBar (the Tab object's original parent). This occurs directly after calling setParent(0) on the Tab.

Basic mouse handling overview:

void Tab::mousePressEvent(*) {
    [set up some boolean, start positions, etc]
}
void Tab::mouseMoveEvent(*) {
    [track the updated position]
    if (positionChange > STATIC_AMOUNT)
        detachTab();
}
void Tab::mouseReleaseEvent(*) {
    [return the Tab to its original position, and set the parent back to the TabBar]
}
void Tab::detachTab() {
    QPoint mappedPos = mapToGlobal(0, 0);
    setParent(0);    //The loss of MouseMove events occurs when this returns.
    move(mappedPos);
    show();
    raise();
}

Here are the events that the Tab object receives (first row is QEvent type, second is the name)

[Tab::detachTab() started]
[setParent(0) started]
QEvent::Hide
QEvent::Leave
qApp QEvent::MouseMove [ TabBar ]    <--  now the TabBar is soaking up the mouse events
QEvent::HideToParent
QEvent::ParentAboutToChange
QEvent::ParentChange
[setParent(0) returned]
....

Summed up: my draggable QWidget loses QEvent::MouseMove and QEvent::MouseButtonRelease events after having its parent set to 0.

Any advice would be really appreciated!

2
Probably, you need to use grabMouse method directly. But i'm not sure that grabbing will not be released when you will dedach a widget.Dmitry Sazonov

2 Answers

2
votes

A bit tricky workaround. I didn't test it, it's just an idea.

When your mouse hovers draggable part of a widget you may create topmost widget (let's call it Shade) with Qt::FramelessWindowHint (and possible with Qt::WA_TranslucentBackground). You may manipulate with Shade apperance via reimplementing paintEvent. For example - draw content of original widget, or draw some transparent preview, etc.

Then you may resize a Shade during dragging, to show user that widget will be detached. You will not loose mouse capture.

When user release mouse - you remember position of Shade, destroy it and detach+move original widget.


Feel free to ask, if you want more details.

0
votes

Here is similar question.
So you suppose to use QDocWidget and enforce stacking of this widgets using tabifyDockWidget.