浏览代码

Merge branch 'popup-widget'

* popup-widget:
  Fix unpinning ctkPopupWidget
  Remove warning where all the switch case were not handled
  Support popups in ctkPopupWidget
  Add test case when popup widget must move
Julien Finet 14 年之前
父节点
当前提交
598589bb46
共有 3 个文件被更改,包括 119 次插入19 次删除
  1. 55 4
      Libs/Widgets/Testing/Cpp/ctkPopupWidgetTest1.cpp
  2. 62 15
      Libs/Widgets/ctkPopupWidget.cpp
  3. 2 0
      Libs/Widgets/ctkPopupWidget_p.h

+ 55 - 4
Libs/Widgets/Testing/Cpp/ctkPopupWidgetTest1.cpp

@@ -21,13 +21,17 @@
 // Qt includes
 // Qt includes
 #include <QApplication>
 #include <QApplication>
 #include <QComboBox>
 #include <QComboBox>
-#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QMenu>
 #include <QPushButton>
 #include <QPushButton>
 #include <QSlider>
 #include <QSlider>
 #include <QTimer>
 #include <QTimer>
+#include <QToolButton>
+#include <QVBoxLayout>
 
 
 // CTK includes
 // CTK includes
 #include "ctkCallback.h"
 #include "ctkCallback.h"
+#include "ctkCollapsibleButton.h"
 #include "ctkPopupWidget.h"
 #include "ctkPopupWidget.h"
 
 
 // STD includes
 // STD includes
@@ -38,31 +42,58 @@
 QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
 QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
 {
 {
   QWidget* topLevel = new QWidget(0);
   QWidget* topLevel = new QWidget(0);
+  topLevel->setObjectName("topLevelWidget");
   topLevel->setWindowTitle(title);
   topLevel->setWindowTitle(title);
+  ctkCollapsibleButton* button = new ctkCollapsibleButton;
   
   
   QComboBox* focusComboBox = new QComboBox;
   QComboBox* focusComboBox = new QComboBox;
+  focusComboBox->setObjectName("focusComboBox");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   focusComboBox->addItem("Focus popup");
   QPushButton* openButton = new QPushButton("Open popup");
   QPushButton* openButton = new QPushButton("Open popup");
+  openButton->setObjectName("openButton");
   QPushButton* toggleButton = new QPushButton("Toggle popup");
   QPushButton* toggleButton = new QPushButton("Toggle popup");
+  toggleButton->setObjectName("toggleButton");
   toggleButton->setCheckable(true);
   toggleButton->setCheckable(true);
+  QToolButton* pinButton = new QToolButton(0);
+  pinButton->setCheckable(true);
+
+  QVBoxLayout* collapsibleLayout = new QVBoxLayout;
+  collapsibleLayout->addWidget(focusComboBox);
+  button->setLayout(collapsibleLayout);
 
 
   QVBoxLayout* vlayout = new QVBoxLayout;
   QVBoxLayout* vlayout = new QVBoxLayout;
-  vlayout->addWidget(focusComboBox);
+  vlayout->addWidget(button);
   vlayout->addWidget(openButton);
   vlayout->addWidget(openButton);
   vlayout->addWidget(toggleButton);
   vlayout->addWidget(toggleButton);
+  vlayout->addWidget(pinButton);
   topLevel->setLayout(vlayout);
   topLevel->setLayout(vlayout);
 
 
   ctkPopupWidget* focusPopup = new ctkPopupWidget;
   ctkPopupWidget* focusPopup = new ctkPopupWidget;
+  focusPopup->setObjectName("focusPopup");
   focusPopup->setAutoShow(true);
   focusPopup->setAutoShow(true);
   focusPopup->setAutoHide(true);
   focusPopup->setAutoHide(true);
   QPushButton* focusPopupContent = new QPushButton("button");
   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(focusPopupContent);
+  focusLayout->addWidget(popupToolButton);
   focusPopup->setLayout(focusLayout);
   focusPopup->setLayout(focusLayout);
   focusPopup->setBaseWidget(focusComboBox);
   focusPopup->setBaseWidget(focusComboBox);
+  focusLayout->setContentsMargins(0,0,0,0);
 
 
   QPalette palette = focusPopup->palette();
   QPalette palette = focusPopup->palette();
   QLinearGradient linearGradient(QPointF(0.f, 0.f), QPointF(0.f, 0.666f));
   QLinearGradient linearGradient(QPointF(0.f, 0.f), QPointF(0.f, 0.666f));
@@ -74,12 +105,14 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
   focusPopup->setPalette(palette);
   focusPopup->setPalette(palette);
 
 
   ctkPopupWidget* openPopup = new ctkPopupWidget;
   ctkPopupWidget* openPopup = new ctkPopupWidget;
+  openPopup->setObjectName("openPopup");
   openPopup->setFrameStyle(QFrame::Box);
   openPopup->setFrameStyle(QFrame::Box);
   openPopup->setLineWidth(1);
   openPopup->setLineWidth(1);
   openPopup->setAutoShow(false);
   openPopup->setAutoShow(false);
   openPopup->setAutoHide(false);
   openPopup->setAutoHide(false);
   openPopup->setWindowOpacity(0.7);
   openPopup->setWindowOpacity(0.7);
   QPushButton* openPopupContent = new QPushButton("Close popup");
   QPushButton* openPopupContent = new QPushButton("Close popup");
+  openPopupContent->setObjectName("openPopupContent");
   QVBoxLayout* openLayout = new QVBoxLayout;
   QVBoxLayout* openLayout = new QVBoxLayout;
   openLayout->addWidget(openPopupContent);
   openLayout->addWidget(openPopupContent);
   openPopup->setLayout(openLayout);
   openPopup->setLayout(openLayout);
@@ -90,9 +123,11 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
                    openPopup, SLOT(hidePopup()));
                    openPopup, SLOT(hidePopup()));
                    
                    
   ctkPopupWidget* togglePopup = new ctkPopupWidget;
   ctkPopupWidget* togglePopup = new ctkPopupWidget;
+  togglePopup->setObjectName("togglePopup");
   togglePopup->setAutoShow(false);
   togglePopup->setAutoShow(false);
   togglePopup->setAutoHide(false);
   togglePopup->setAutoHide(false);
   QPushButton* togglePopupContent = new QPushButton("useless button");
   QPushButton* togglePopupContent = new QPushButton("useless button");
+  togglePopupContent->setObjectName("togglePopupContent");
   QVBoxLayout* toggleLayout = new QVBoxLayout;
   QVBoxLayout* toggleLayout = new QVBoxLayout;
   toggleLayout->addWidget(togglePopupContent);
   toggleLayout->addWidget(togglePopupContent);
   togglePopup->setLayout(toggleLayout);
   togglePopup->setLayout(toggleLayout);
@@ -101,7 +136,23 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
                    togglePopup, SLOT(showPopup(bool)));
                    togglePopup, SLOT(showPopup(bool)));
   togglePopup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
   togglePopup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
   
   
-  popups << focusPopup << openPopup << togglePopup;
+  ctkPopupWidget* pinPopup = new ctkPopupWidget;
+  pinPopup->setObjectName("pinPopup");
+  pinPopup->setBaseWidget(pinButton);
+  QPushButton* pinPopupContent = new QPushButton("pin button");
+  pinPopupContent->setCheckable(true);
+  QObject::connect(pinPopupContent, SIGNAL(toggled(bool)),
+                   pinButton, SLOT(setChecked(bool)));
+  QObject::connect(pinButton, SIGNAL(toggled(bool)),
+                   pinPopupContent, SLOT(setChecked(bool)));
+  pinPopupContent->setObjectName("pinPopupContent");
+  QVBoxLayout* pinLayout = new QVBoxLayout;
+  pinLayout->addWidget(pinPopupContent);
+  pinPopup->setLayout(pinLayout);
+  QObject::connect(pinButton, SIGNAL(toggled(bool)),
+                   pinPopup, SLOT(pinPopup(bool)));
+  
+  popups << focusPopup << openPopup << togglePopup << pinPopup;
   return topLevel;
   return topLevel;
 }
 }
 
 

+ 62 - 15
Libs/Widgets/ctkPopupWidget.cpp

@@ -29,6 +29,7 @@
 #include <QMouseEvent>
 #include <QMouseEvent>
 #include <QMoveEvent>
 #include <QMoveEvent>
 #include <QPainter>
 #include <QPainter>
+#include <QPointer>
 #include <QPropertyAnimation>
 #include <QPropertyAnimation>
 #include <QStyle>
 #include <QStyle>
 #include <QTimer>
 #include <QTimer>
@@ -184,14 +185,17 @@ bool ctkPopupWidgetPrivate::mouseOver()
       }
       }
     }
     }
   // Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
   // 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)
   foreach(const QWidget* focusWidget, widgets)
     {
     {
     if (this->isAncestorOf(focusWidget, widgetUnderCursor) &&
     if (this->isAncestorOf(focusWidget, widgetUnderCursor) &&
         // Ignore when cursor is above a title bar of a focusWidget, underMouse
         // Ignore when cursor is above a title bar of a focusWidget, underMouse
         // wouldn't have return false, but QApplication::widgetAt would return
         // wouldn't have return false, but QApplication::widgetAt would return
         // the widget
         // the widget
-        focusWidget != widgetUnderCursor)
+        (focusWidget != widgetUnderCursor ||
+         QRect(QPoint(0,0), focusWidget->size()).contains(
+          focusWidget->mapFromGlobal(pos))))
       {
       {
       widgetUnderCursor->installEventFilter(q);
       widgetUnderCursor->installEventFilter(q);
       return true;
       return true;
@@ -478,6 +482,8 @@ void ctkPopupWidgetPrivate::updateVisibility()
           topLevelWidget != (this->BaseWidget ? this->BaseWidget->window() : 0) &&
           topLevelWidget != (this->BaseWidget ? this->BaseWidget->window() : 0) &&
           topLevelWidget->frameGeometry().intersects(q->geometry()))
           topLevelWidget->frameGeometry().intersects(q->geometry()))
         {
         {
+        //qDebug() << "hide" << q << "because of: " << topLevelWidget
+        //         << " with windowType: " << topLevelWidget->windowType();
         this->temporarilyHiddenOn();
         this->temporarilyHiddenOn();
         return;
         return;
         }
         }
@@ -499,7 +505,7 @@ void ctkPopupWidgetPrivate::updateVisibility()
 void ctkPopupWidgetPrivate::onBaseWidgetDestroyed()
 void ctkPopupWidgetPrivate::onBaseWidgetDestroyed()
 {
 {
   Q_Q(ctkPopupWidget);
   Q_Q(ctkPopupWidget);
-  q->hide();
+  this->hideAll();
   q->setBaseWidget(0);
   q->setBaseWidget(0);
   // could be a property.
   // could be a property.
   q->deleteLater();
   q->deleteLater();
@@ -516,8 +522,7 @@ void ctkPopupWidgetPrivate::temporarilyHiddenOn()
     this->setProperty("forcedClosed", this->isOpening() ? 2 : 1);
     this->setProperty("forcedClosed", this->isOpening() ? 2 : 1);
     }
     }
   this->currentAnimation()->stop();
   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::FramelessWindowHint is required on Windows for Translucent background
 // Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
 // 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)
 // click outside the widget (typically a click in the BaseWidget)
@@ -713,15 +742,14 @@ void ctkPopupWidget::onEffectFinished()
     }
     }
   if (qobject_cast<QAbstractAnimation*>(this->sender())->direction() == QAbstractAnimation::Backward)
   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())
+    d->hideAll();
+    emit this->popupOpened(false);
+    /// restore the AutoShow if needed.
+    if (!this->property("AutoShowOnClose").isNull())
       {
       {
-      qApp->setActiveWindow(d->BaseWidget ? d->BaseWidget->window() : 0);
+      d->AutoShow = this->property("AutoShowOnClose").toBool();
+      this->setProperty("AutoShowOnClose", QVariant());
       }
       }
-    this->hide();
-    emit this->popupOpened(false);
     }
     }
   else
   else
     {
     {
@@ -799,10 +827,15 @@ bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
 	    }	    
 	    }	    
     case QEvent::Hide:
     case QEvent::Hide:
     case QEvent::Close:
     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();
       d->temporarilyHiddenOn();
 	    break;
 	    break;
     case QEvent::Show:
     case QEvent::Show:
@@ -838,12 +871,22 @@ bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
         }
         }
       break;
       break;
     case QEvent::Leave:
     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()));
       QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
       if (obj != d->BaseWidget)
       if (obj != d->BaseWidget)
         {
         {
         obj->removeEventFilter(this);
         obj->removeEventFilter(this);
         }
         }
       break;
       break;
+    default:
+      break;
     }
     }
   return this->QObject::eventFilter(obj, event);
   return this->QObject::eventFilter(obj, event);
 }
 }
@@ -988,6 +1031,7 @@ void ctkPopupWidget::hidePopup()
 // --------------------------------------------------------------------------
 // --------------------------------------------------------------------------
 void ctkPopupWidget::pinPopup(bool pin)
 void ctkPopupWidget::pinPopup(bool pin)
 {
 {
+  Q_D(ctkPopupWidget);
   this->setAutoHide(!pin);
   this->setAutoHide(!pin);
   if (pin)
   if (pin)
     {
     {
@@ -995,6 +1039,9 @@ void ctkPopupWidget::pinPopup(bool pin)
     }
     }
   else
   else
     {
     {
+    // When closing, we don't want to inadvertently re-open the menu.
+    this->setProperty("AutoShowOnClose", this->autoShow());
+    d->AutoShow = false;
     this->hidePopup();
     this->hidePopup();
     }
     }
 }
 }

+ 2 - 0
Libs/Widgets/ctkPopupWidget_p.h

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