Popup with toggle button
-
I'm trying to implement the following behavior: The main window has a button which can toggle the appearance of a popup (derived from QWidget with the Qt::Popup window flag set. To close the popup the user can either click on the button again or somewhere outside of the popup. The problem I have is that if the user clicks on the button again, the popup is quickly hidden, but then shown again. It seems that Qt first hides the popup, because the user clicked outside of the popup and then shows it again, because the user clicked on the button. I tried to disable the button if the window is shown, but this doesn't work, because the hideEvent(..) of the popup happens before the button emits the clicked() signal. I also tried to check on the hideEvent weather the cursor is over the button by implementing the mouseEnter/-leave events of this buttons. This however doesn't seem to work reliable, because when the popup is in forderground the button does not seem to receive the enter/leave events.
-
I tried to solve it using event filters. I wanted the mouse click event to stop propagating up to the show hide button. So I installed the following event filter in my popup widget:
@bool MouseClickOutsideEventFilter::eventFilter( QObject *obj, QEvent *event )
{
if ( typeid(*obj) != typeid(MyPopup) )
return false;if ( event->type() == QEvent::MouseButtonPress ) {
MyPopup popup = dynamic_cast<MyPopup>(obj);
popup->hide();
event->accept();
return true;
}return false;
}@
Returning true upon receiving the MouseButtonPress event should stop it. I also accept()-ed it, however the filter had no effect :( Right now I worked around this problem by using a timer which waits 200ms after hiding the popup then sets a flag which enables the slot connected to the button's clicked() signal again. This of course is a rather poor solution.
-
Hm so your "popup" isn't really a popup menu but more the popup container that a QComboBox creates when clicked, right?
I've looked through "QComboBox code":http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/widgets/qcombobox.cpp and the interesting class here is QComboBoxPrivateContainer. That's the "popup" part of the combobox. You, too, should subclass the button that creates such a drop-down container and make your dropdown-widget a member of the button. Further, don't set the windowFlag Qt::Popup, as this will take control away from you to decide when the widget is hidden. The QComboBoxPrivateContainer controls when it shall be closed itself in the QComboBoxPrivateContainer::eventFilter function (see the branch starting at line 684.)
In Essence they capture MouseButtonRelease event, check whether the event happened inside the rect of the container and if not, hide the container (making the container member of the button also allows the toggling of the popup upon successive clicking of the button, it might even be desirable for the container to be on top (hide) the button while it's opened, like QComboBox does). -
These are some interesting thoughts. I guess I really have to read some Qt code, before I think of a better way to do it. Thx for your answer!
-
I also tried to use the QComboBox with a QTreeWidget. It basically works because you can set the view of the QComboBox:
@
myComboBox->setModel(treeWidget->model());
myComboBox->setView(treeWidget);
myComboBox->setFrame(false);
@However whenever I click on something in the TreeView the popup is hidden. This made sense for the entries in the QListView, which the QComboBox uses by default but not for the TreeView. I want it to only hide if the user double-clicks an entry and so far I could not figure out how to do this. Closing happens in the event filter of the QComboBoxPrivateContainer. So I tried first to install an event filter which returns true on the MouseReleaseEvent on the view.
@FilterObj *filter = new FilterObj(m_mainWin);
myComboBox->view()->installEventFilter(filter);@The problem is that the eventFilter on the QComboBoxPrivateContainer gets called first. So I also tried to overwrite QCombBoxes event(..) method but also w/o any success. To me it seems that I would need to modify the QComboBoxPrivateContainer in order to change this behavior. However because it is private I am not supposed to do this. For me this raises two questions:
- How can I figure out the order in which an event is processed? Which widget gets it first and to which widget is it passed?
- Is there another way to change the MouseButtonRelease event-processing behavior of the QComboBox?