Преглед на файлове

Support popups in ctkPopupWidget

Julien Finet преди 14 години
родител
ревизия
39fbc99f11
променени са 3 файла, в които са добавени 81 реда и са изтрити 18 реда
  1. 28 2
      Libs/Widgets/Testing/Cpp/ctkPopupWidgetTest1.cpp
  2. 51 16
      Libs/Widgets/ctkPopupWidget.cpp
  3. 2 0
      Libs/Widgets/ctkPopupWidget_p.h

+ 28 - 2
Libs/Widgets/Testing/Cpp/ctkPopupWidgetTest1.cpp

@@ -21,10 +21,13 @@
 // Qt includes
 #include <QApplication>
 #include <QComboBox>
-#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QMenu>
 #include <QPushButton>
 #include <QSlider>
 #include <QTimer>
+#include <QToolButton>
+#include <QVBoxLayout>
 
 // CTK includes
 #include "ctkCallback.h"
@@ -39,16 +42,20 @@
 QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
 {
   QWidget* topLevel = new QWidget(0);
+  topLevel->setObjectName("topLevelWidget");
   topLevel->setWindowTitle(title);
   ctkCollapsibleButton* button = new ctkCollapsibleButton;
   
   QComboBox* focusComboBox = new QComboBox;
+  focusComboBox->setObjectName("focusComboBox");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   QPushButton* openButton = new QPushButton("Open popup");
+  openButton->setObjectName("openButton");
   QPushButton* toggleButton = new QPushButton("Toggle popup");
+  toggleButton->setObjectName("toggleButton");
   toggleButton->setCheckable(true);
 
   QVBoxLayout* collapsibleLayout = new QVBoxLayout;
@@ -62,13 +69,28 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
   topLevel->setLayout(vlayout);
 
   ctkPopupWidget* focusPopup = new ctkPopupWidget;
+  focusPopup->setObjectName("focusPopup");
   focusPopup->setAutoShow(true);
   focusPopup->setAutoHide(true);
   QPushButton* focusPopupContent = new QPushButton("button");
-  QVBoxLayout* focusLayout = new QVBoxLayout;
+  focusPopupContent->setObjectName("focusPopupContent");
+  QToolButton* popupToolButton = new QToolButton;
+  popupToolButton->setObjectName("popupToolButton");
+  QMenu* menu = new QMenu(popupToolButton);
+  menu->setObjectName("menu");
+  menu->addAction("first menu item");
+  menu->addAction("second menu item");
+  menu->addAction("third menu item");
+  menu->addAction("fourth menu item");
+  popupToolButton->setPopupMode(QToolButton::InstantPopup);
+  popupToolButton->setMenu(menu);
+
+  QHBoxLayout* focusLayout = new QHBoxLayout;
   focusLayout->addWidget(focusPopupContent);
+  focusLayout->addWidget(popupToolButton);
   focusPopup->setLayout(focusLayout);
   focusPopup->setBaseWidget(focusComboBox);
+  focusLayout->setContentsMargins(0,0,0,0);
 
   QPalette palette = focusPopup->palette();
   QLinearGradient linearGradient(QPointF(0.f, 0.f), QPointF(0.f, 0.666f));
@@ -80,12 +102,14 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
   focusPopup->setPalette(palette);
 
   ctkPopupWidget* openPopup = new ctkPopupWidget;
+  openPopup->setObjectName("openPopup");
   openPopup->setFrameStyle(QFrame::Box);
   openPopup->setLineWidth(1);
   openPopup->setAutoShow(false);
   openPopup->setAutoHide(false);
   openPopup->setWindowOpacity(0.7);
   QPushButton* openPopupContent = new QPushButton("Close popup");
+  openPopupContent->setObjectName("openPopupContent");
   QVBoxLayout* openLayout = new QVBoxLayout;
   openLayout->addWidget(openPopupContent);
   openPopup->setLayout(openLayout);
@@ -96,9 +120,11 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
                    openPopup, SLOT(hidePopup()));
                    
   ctkPopupWidget* togglePopup = new ctkPopupWidget;
+  togglePopup->setObjectName("togglePopup");
   togglePopup->setAutoShow(false);
   togglePopup->setAutoHide(false);
   QPushButton* togglePopupContent = new QPushButton("useless button");
+  togglePopupContent->setObjectName("togglePopupContent");
   QVBoxLayout* toggleLayout = new QVBoxLayout;
   toggleLayout->addWidget(togglePopupContent);
   togglePopup->setLayout(toggleLayout);

+ 51 - 16
Libs/Widgets/ctkPopupWidget.cpp

@@ -29,6 +29,7 @@
 #include <QMouseEvent>
 #include <QMoveEvent>
 #include <QPainter>
+#include <QPointer>
 #include <QPropertyAnimation>
 #include <QStyle>
 #include <QTimer>
@@ -184,14 +185,17 @@ bool ctkPopupWidgetPrivate::mouseOver()
       }
     }
   // Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
-  QWidget* widgetUnderCursor = qApp->widgetAt(QCursor::pos());
+  const QPoint pos = QCursor::pos();
+  QWidget* widgetUnderCursor = qApp->widgetAt(pos);
   foreach(const QWidget* focusWidget, widgets)
     {
     if (this->isAncestorOf(focusWidget, widgetUnderCursor) &&
         // Ignore when cursor is above a title bar of a focusWidget, underMouse
         // wouldn't have return false, but QApplication::widgetAt would return
         // the widget
-        focusWidget != widgetUnderCursor)
+        (focusWidget != widgetUnderCursor ||
+         QRect(QPoint(0,0), focusWidget->size()).contains(
+          focusWidget->mapFromGlobal(pos))))
       {
       widgetUnderCursor->installEventFilter(q);
       return true;
@@ -478,6 +482,8 @@ void ctkPopupWidgetPrivate::updateVisibility()
           topLevelWidget != (this->BaseWidget ? this->BaseWidget->window() : 0) &&
           topLevelWidget->frameGeometry().intersects(q->geometry()))
         {
+        //qDebug() << "hide" << q << "because of: " << topLevelWidget
+        //         << " with windowType: " << topLevelWidget->windowType();
         this->temporarilyHiddenOn();
         return;
         }
@@ -499,7 +505,7 @@ void ctkPopupWidgetPrivate::updateVisibility()
 void ctkPopupWidgetPrivate::onBaseWidgetDestroyed()
 {
   Q_Q(ctkPopupWidget);
-  q->hide();
+  this->hideAll();
   q->setBaseWidget(0);
   // could be a property.
   q->deleteLater();
@@ -516,8 +522,7 @@ void ctkPopupWidgetPrivate::temporarilyHiddenOn()
     this->setProperty("forcedClosed", this->isOpening() ? 2 : 1);
     }
   this->currentAnimation()->stop();
-  this->PopupPixmapWidget->hide();
-  q->hide();
+  this->hideAll();
 }
 
 // -------------------------------------------------------------------------
@@ -542,6 +547,30 @@ void ctkPopupWidgetPrivate::temporarilyHiddenOff()
 }
 
 // -------------------------------------------------------------------------
+void ctkPopupWidgetPrivate::hideAll()
+{
+  Q_Q(ctkPopupWidget);
+  // Before hiding, transfer the active window flag to its parent, this will
+  // prevent the application to send a ApplicationDeactivate signal that
+  // doesn't need to be done.
+  if (q->isActiveWindow() && this->BaseWidget)
+    {
+    qApp->setActiveWindow(this->BaseWidget->window());
+    }
+
+  q->hide();
+  this->PopupPixmapWidget->hide();
+
+  // If there is a popup open in the ctkPopupWidget children, then hide it
+  // as well so we don't have a popup open while the ctkPopupWidget is hidden.
+  QPointer<QWidget> activePopupWidget = qApp->activePopupWidget();
+  if (activePopupWidget && this->isAncestorOf(q, activePopupWidget))
+    {
+    activePopupWidget->close();
+    }
+}
+
+// -------------------------------------------------------------------------
 // Qt::FramelessWindowHint is required on Windows for Translucent background
 // Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
 // click outside the widget (typically a click in the BaseWidget)
@@ -713,14 +742,7 @@ void ctkPopupWidget::onEffectFinished()
     }
   if (qobject_cast<QAbstractAnimation*>(this->sender())->direction() == QAbstractAnimation::Backward)
     {
-    // Before hiding, transfer the active window flag to its parent, this will
-    // prevent the application to send a ApplicationDeactivate signal that doesn't
-    // need to be done.
-    if (this->isActiveWindow())
-      {
-      qApp->setActiveWindow(d->BaseWidget ? d->BaseWidget->window() : 0);
-      }
-    this->hide();
+    d->hideAll();
     emit this->popupOpened(false);
     }
   else
@@ -799,10 +821,15 @@ bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
 	    }	    
     case QEvent::Hide:
     case QEvent::Close:
-      if (obj != d->BaseWidget)
+      // if the mouse was in a base widget child popup, then when we leave
+      // the popup we want to check if it needs to be closed.
+      if (obj != d->BaseWidget  &&
+          qobject_cast<QWidget*>(obj)->windowType() == Qt::Popup)
         {
-	      break;
-	      }
+        QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
+        obj->removeEventFilter(this);
+        break;
+        }
       d->temporarilyHiddenOn();
 	    break;
     case QEvent::Show:
@@ -838,6 +865,14 @@ bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
         }
       break;
     case QEvent::Leave:
+      // Don't listen to base widget children that are popups as what
+      // matters here is their close event instead
+      if (obj != d->BaseWidget &&
+          qobject_cast<QWidget*>(obj)->windowType() == Qt::Popup)
+        {
+        break;
+        }
+      // The mouse might have left the area that keeps the popup open
       QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
       if (obj != d->BaseWidget)
         {

+ 2 - 0
Libs/Widgets/ctkPopupWidget_p.h

@@ -76,6 +76,8 @@ public:
   void temporarilyHiddenOn();
   void temporarilyHiddenOff();
 
+  void hideAll();
+
 public slots:
   void updateVisibility();
   void onBaseWidgetDestroyed();