Browse Source

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 years ago
parent
commit
598589bb46

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

@@ -21,13 +21,17 @@
 // 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"
+#include "ctkCollapsibleButton.h"
 #include "ctkPopupWidget.h"
 
 // STD includes
@@ -38,31 +42,58 @@
 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);
+  QToolButton* pinButton = new QToolButton(0);
+  pinButton->setCheckable(true);
+
+  QVBoxLayout* collapsibleLayout = new QVBoxLayout;
+  collapsibleLayout->addWidget(focusComboBox);
+  button->setLayout(collapsibleLayout);
 
   QVBoxLayout* vlayout = new QVBoxLayout;
-  vlayout->addWidget(focusComboBox);
+  vlayout->addWidget(button);
   vlayout->addWidget(openButton);
   vlayout->addWidget(toggleButton);
+  vlayout->addWidget(pinButton);
   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));
@@ -74,12 +105,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);
@@ -90,9 +123,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);
@@ -101,7 +136,23 @@ QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
                    togglePopup, SLOT(showPopup(bool)));
   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;
 }
 

+ 62 - 15
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,15 +742,14 @@ 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())
+    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
     {
@@ -799,10 +827,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,12 +871,22 @@ 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)
         {
         obj->removeEventFilter(this);
         }
       break;
+    default:
+      break;
     }
   return this->QObject::eventFilter(obj, event);
 }
@@ -988,6 +1031,7 @@ void ctkPopupWidget::hidePopup()
 // --------------------------------------------------------------------------
 void ctkPopupWidget::pinPopup(bool pin)
 {
+  Q_D(ctkPopupWidget);
   this->setAutoHide(!pin);
   if (pin)
     {
@@ -995,6 +1039,9 @@ void ctkPopupWidget::pinPopup(bool pin)
     }
   else
     {
+    // When closing, we don't want to inadvertently re-open the menu.
+    this->setProperty("AutoShowOnClose", this->autoShow());
+    d->AutoShow = false;
     this->hidePopup();
     }
 }

+ 2 - 0
Libs/Widgets/ctkPopupWidget_p.h

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