| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557 | 
							- /*=========================================================================
 
-   Library:   CTK
 
-   Copyright (c) Kitware Inc.
 
-   Licensed under the Apache License, Version 2.0 (the "License");
 
-   you may not use this file except in compliance with the License.
 
-   You may obtain a copy of the License at
 
-       http://www.apache.org/licenses/LICENSE-2.0.txt
 
-   Unless required by applicable law or agreed to in writing, software
 
-   distributed under the License is distributed on an "AS IS" BASIS,
 
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
-   See the License for the specific language governing permissions and
 
-   limitations under the License.
 
- =========================================================================*/
 
- // Qt includes
 
- #include <QApplication>
 
- #include <QDebug>
 
- #include <QDesktopWidget>
 
- #include <QDir>
 
- #include <QEvent>
 
- #include <QLabel>
 
- #include <QLayout>
 
- #include <QMouseEvent>
 
- #include <QMoveEvent>
 
- #include <QPainter>
 
- #include <QPointer>
 
- #include <QPropertyAnimation>
 
- #include <QStyle>
 
- #include <QTimer>
 
- // CTK includes
 
- #include "ctkPopupWidget_p.h"
 
- // -------------------------------------------------------------------------
 
- ctkPopupWidgetPrivate::ctkPopupWidgetPrivate(ctkPopupWidget& object)
 
-   :Superclass(object)
 
- {
 
-   this->Active = false;
 
-   this->AutoShow = true;
 
-   this->ShowDelay = 20;
 
-   this->AutoHide = true;
 
-   this->HideDelay = 200;
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkPopupWidgetPrivate::~ctkPopupWidgetPrivate()
 
- {
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidgetPrivate::init()
 
- {
 
-   Q_Q(ctkPopupWidget);
 
-   this->setParent(q);
 
-   q->setActive(true);
 
-   this->Superclass::init();
 
- }
 
- // -------------------------------------------------------------------------
 
- QWidget* ctkPopupWidgetPrivate::mouseOver()
 
- {
 
-   Q_Q(ctkPopupWidget);
 
-   QWidget* widgetUnderCursor = this->Superclass::mouseOver();
 
-   if (widgetUnderCursor &&
 
-       !this->focusWidgets(true).contains(widgetUnderCursor))
 
-     {
 
-     widgetUnderCursor->installEventFilter(q);
 
-     }
 
-   return widgetUnderCursor;
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkPopupWidgetPrivate::eventFilter(QObject* obj, QEvent* event)
 
- {
 
-   Q_Q(ctkPopupWidget);
 
-   QWidget* widget = qobject_cast<QWidget*>(obj);
 
-   // Here are the application events, it's a lot of events, so we need to be
 
-   // careful to be fast.
 
-   if (event->type() == QEvent::ApplicationDeactivate)
 
-     {
 
-     // We wait to see if there is no other window being active
 
-     QTimer::singleShot(0, this, SLOT(onApplicationDeactivate()));
 
-     }
 
-   else if (event->type() == QEvent::ApplicationActivate)
 
-     {
 
-     QTimer::singleShot(0, this, SLOT(updateVisibility()));
 
-     }
 
-   if (!this->BaseWidget)
 
-     {
 
-     return false;
 
-     }
 
-   if (event->type() == QEvent::Move && widget != this->BaseWidget)
 
-     {
 
-     if (widget->isAncestorOf(this->BaseWidget))
 
-       {
 
-       QMoveEvent* moveEvent = dynamic_cast<QMoveEvent*>(event);
 
-       QPoint topLeft = widget->parentWidget() ? widget->parentWidget()->mapToGlobal(moveEvent->pos()) : moveEvent->pos();
 
-       topLeft += this->BaseWidget->mapTo(widget, QPoint(0,0));
 
-       //q->move(q->pos() + moveEvent->pos() - moveEvent->oldPos());
 
-       QRect newBaseGeometry = this->baseGeometry();
 
- 	    newBaseGeometry.moveTopLeft(topLeft);
 
- 	    QRect desiredGeometry = this->desiredOpenGeometry(newBaseGeometry);
 
- 	    q->move(desiredGeometry.topLeft());
 
-       }
 
-     else if (widget->isWindow() &&
 
-              widget->windowType() != Qt::ToolTip &&
 
-              widget->windowType() != Qt::Popup)
 
-       {
 
-       QTimer::singleShot(0, this, SLOT(updateVisibility()));
 
-       }
 
-     }
 
-   else if (event->type() == QEvent::Resize)
 
-     {
 
-     if (widget->isWindow() &&
 
-         widget != this->BaseWidget->window() &&
 
-         widget->windowType() != Qt::ToolTip &&
 
-         widget->windowType() != Qt::Popup)
 
-       {
 
-       QTimer::singleShot(0, this, SLOT(updateVisibility()));
 
-       }
 
-     }
 
-   else if (event->type() == QEvent::WindowStateChange &&
 
-            widget != this->BaseWidget->window() &&
 
-            widget->windowType() != Qt::ToolTip &&
 
-            widget->windowType() != Qt::Popup)
 
-     {
 
-     QTimer::singleShot(0, this, SLOT(updateVisibility()));
 
-     }
 
-   else if ((event->type() == QEvent::WindowActivate ||
 
-             event->type() == QEvent::WindowDeactivate) &&
 
-            widget == this->BaseWidget->window())
 
-     {
 
-     QTimer::singleShot(0, this, SLOT(updateVisibility()));
 
-     }
 
-   else if (event->type() == QEvent::RequestSoftwareInputPanel)
 
-     {
 
-     qApp->setActiveWindow(widget->window());
 
-     }
 
-   return false;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidgetPrivate::onApplicationDeactivate()
 
- {
 
-   // Still no active window, that means the user now is controlling another
 
-   // application, we have no control over when the other app moves over the
 
-   // popup, so we hide the popup as it would show on top of the other app.
 
-   if (!qApp->activeWindow())
 
-     {
 
-     this->temporarilyHiddenOn();
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidgetPrivate::updateVisibility()
 
- {
 
-   Q_Q(ctkPopupWidget);
 
-   // If the BaseWidget window is active, then there is no reason to cover the
 
-   // popup.
 
-   if (!this->BaseWidget  ||
 
-       // the popupwidget active window is not active
 
-       (!this->BaseWidget->window()->isActiveWindow() &&
 
-       // and no other active window
 
-        (!qApp->activeWindow() ||
 
-       // or the active window is a popup/tooltip
 
-         (qApp->activeWindow()->windowType() != Qt::ToolTip &&
 
-          qApp->activeWindow()->windowType() != Qt::Popup))))
 
-     {
 
-     foreach(QWidget* topLevelWidget, qApp->topLevelWidgets())
 
-       {
 
-       // If there is at least 1 window (active or not) that covers the popup,
 
-       // then we ensure the popup is hidden.
 
-       // We have no way of knowing which toplevel is over (z-order) which one,
 
-       // it is an OS specific information.
 
-       // Of course, tooltips and popups don't count as covering windows.
 
-       if (topLevelWidget->isVisible() &&
 
-           !(topLevelWidget->windowState() & Qt::WindowMinimized) &&
 
-           topLevelWidget->windowType() != Qt::ToolTip &&
 
-           topLevelWidget->windowType() != Qt::Popup &&
 
-           topLevelWidget != (this->BaseWidget ? this->BaseWidget->window() : 0) &&
 
-           topLevelWidget->frameGeometry().intersects(q->geometry()))
 
-         {
 
-         //qDebug() << "hide" << q << "because of: " << topLevelWidget
 
-         //         << " with windowType: " << topLevelWidget->windowType()
 
-         //         << topLevelWidget->isVisible()
 
-         //         << (this->BaseWidget ? this->BaseWidget->window() : 0)
 
-         //         << topLevelWidget->frameGeometry();
 
-         this->temporarilyHiddenOn();
 
-         return;
 
-         }
 
-       }
 
-     }
 
-   // If the base widget is hidden or minimized, we don't want to restore the
 
-   // popup.
 
-   if (this->BaseWidget &&
 
-       (!this->BaseWidget->isVisible() ||
 
-         this->BaseWidget->window()->windowState() & Qt::WindowMinimized))
 
-     {
 
-     return;
 
-     }
 
-   // Restore the visibility of the popup if it was hidden
 
-   this->temporarilyHiddenOff();
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidgetPrivate::temporarilyHiddenOn()
 
- {
 
-   Q_Q(ctkPopupWidget);
 
-   if (!this->AutoHide &&
 
-       (q->isVisible() || this->isOpening()) &&
 
-       !(q->isHidden() || this->isClosing()))
 
-     {
 
-     this->setProperty("forcedClosed", this->isOpening() ? 2 : 1);
 
-     }
 
-   this->currentAnimation()->stop();
 
-   this->hideAll();
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidgetPrivate::temporarilyHiddenOff()
 
- {
 
-   Q_Q(ctkPopupWidget);
 
-   int forcedClosed = this->property("forcedClosed").toInt();
 
-   if (forcedClosed > 0)
 
-     {
 
-     q->show();
 
-     if (forcedClosed == 2)
 
-       {
 
-       emit q->popupOpened(true);
 
-       }
 
-     this->setProperty("forcedClosed", 0);
 
-     }
 
-   else
 
-     {
 
-     q->updatePopup();
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- // 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)
 
- ctkPopupWidget::ctkPopupWidget(QWidget* parentWidget)
 
-   : Superclass(new ctkPopupWidgetPrivate(*this), parentWidget)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   d->init();
 
- }
 
- // -------------------------------------------------------------------------
 
- ctkPopupWidget::~ctkPopupWidget()
 
- {
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkPopupWidget::isActive()const
 
- {
 
-   Q_D(const ctkPopupWidget);
 
-   return d->Active;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::setActive(bool active)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   if (active == d->Active)
 
-     {
 
-     return;
 
-     }
 
-   d->Active = active;
 
-   if (d->Active)
 
-     {
 
-     if (d->BaseWidget)
 
-       {
 
-       d->BaseWidget->installEventFilter(this);
 
-       }
 
-     if (d->PopupPixmapWidget)
 
-       {
 
-       d->PopupPixmapWidget->installEventFilter(this);
 
-       }
 
-     qApp->installEventFilter(d);
 
-     }
 
-   else // not active
 
-     {
 
-     if (d->BaseWidget)
 
-       {
 
-       d->BaseWidget->removeEventFilter(this);
 
-       }
 
-     if (d->PopupPixmapWidget)
 
-       {
 
-       d->PopupPixmapWidget->removeEventFilter(this);
 
-       }
 
-     qApp->removeEventFilter(d);
 
-     }
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::setBaseWidget(QWidget* widget)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   if (d->BaseWidget)
 
-     {
 
-     d->BaseWidget->removeEventFilter(this);
 
-     }
 
-   this->Superclass::setBaseWidget(widget);
 
-   if (d->BaseWidget && d->Active)
 
-     {
 
-     d->BaseWidget->installEventFilter(this);
 
-     }
 
-   QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkPopupWidget::autoShow()const
 
- {
 
-   Q_D(const ctkPopupWidget);
 
-   return d->AutoShow;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::setAutoShow(bool mode)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   d->AutoShow = mode;
 
-   QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
 
- }
 
- // -------------------------------------------------------------------------
 
- int ctkPopupWidget::showDelay()const
 
- {
 
-   Q_D(const ctkPopupWidget);
 
-   return d->ShowDelay;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::setShowDelay(int delay)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   d->ShowDelay = delay;
 
- }
 
- // -------------------------------------------------------------------------
 
- bool ctkPopupWidget::autoHide()const
 
- {
 
-   Q_D(const ctkPopupWidget);
 
-   return d->AutoHide;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::setAutoHide(bool mode)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   d->AutoHide = mode;
 
-   QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
 
- }
 
- // -------------------------------------------------------------------------
 
- int ctkPopupWidget::hideDelay()const
 
- {
 
-   Q_D(const ctkPopupWidget);
 
-   return d->HideDelay;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::setHideDelay(int delay)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   d->HideDelay = delay;
 
- }
 
- // -------------------------------------------------------------------------
 
- void ctkPopupWidget::onEffectFinished()
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   bool wasClosing = d->wasClosing();
 
-   this->Superclass::onEffectFinished();
 
-   if (wasClosing)
 
-     {
 
-     /// restore the AutoShow if needed.
 
-     if (!this->property("AutoShowOnClose").isNull())
 
-       {
 
-       d->AutoShow = this->property("AutoShowOnClose").toBool();
 
-       this->setProperty("AutoShowOnClose", QVariant());
 
-       }
 
-     }
 
- }
 
- // --------------------------------------------------------------------------
 
- void ctkPopupWidget::leaveEvent(QEvent* event)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
 
-   this->Superclass::leaveEvent(event);
 
- }
 
- // --------------------------------------------------------------------------
 
- void ctkPopupWidget::enterEvent(QEvent* event)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
 
-   this->Superclass::enterEvent(event);
 
- }
 
- // --------------------------------------------------------------------------
 
- bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   // Here we listen to PopupPixmapWidget, BaseWidget and ctkPopupWidget
 
-   // children popups that were under the mouse
 
-   switch(event->type())
 
-     {
 
-     case QEvent::Move:
 
-       {
 
-       if (obj != d->BaseWidget)
 
-         {
 
-         break;
 
-         }
 
-       QMoveEvent* moveEvent = dynamic_cast<QMoveEvent*>(event);
 
-       QRect newBaseGeometry = d->baseGeometry();
 
-       newBaseGeometry.moveTopLeft(d->mapToGlobal(moveEvent->pos()));
 
-       QRect desiredGeometry = d->desiredOpenGeometry(newBaseGeometry);
 
-       this->move(desiredGeometry.topLeft());
 
-       //this->move(this->pos() + moveEvent->pos() - moveEvent->oldPos());
 
-       this->update();
 
-       break;
 
-       }
 
-     case QEvent::Hide:
 
-     case QEvent::Close:
 
-       // 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)
 
-         {
 
-         if (obj != d->PopupPixmapWidget &&
 
-             qobject_cast<QWidget*>(obj)->windowType() == Qt::Popup)
 
-           {
 
-           obj->removeEventFilter(this);
 
-           QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
 
-           }
 
-         break;
 
-         }
 
-       d->temporarilyHiddenOn();
 
-       break;
 
-     case QEvent::Show:
 
-       if (obj != d->BaseWidget)
 
-         {
 
- 	      break;
 
- 	      }
 
- 	    this->setGeometry(d->desiredOpenGeometry());
 
- 	    d->temporarilyHiddenOff();
 
- 	    break;
 
- 	  case QEvent::Resize:
 
- 	    if (obj != d->BaseWidget ||
 
- 	        !(d->Alignment & Qt::AlignJustify ||
 
- 	         (d->Alignment & Qt::AlignTop && d->Alignment & Qt::AlignBottom)) ||
 
- 	         !(d->isOpening() || this->isVisible()))
 
- 	      {
 
- 	      break;
 
- 	      }
 
- 	    // TODO: bug when the effect is WindowOpacityFadeEffect
 
- 	    this->setGeometry(d->desiredOpenGeometry());
 
- 	    break;
 
-     case QEvent::Enter:
 
-       if ( d->currentAnimation()->state() == QAbstractAnimation::Stopped )
 
-         {
 
-         // Maybe the user moved the mouse on the widget by mistake, don't open
 
-         // the popup instantly...
 
-         QTimer::singleShot(d->ShowDelay, this, SLOT(updatePopup()));
 
-         }
 
-       else 
 
-         {
 
-         // ... except if the popup is closing, we want to reopen it as sooon as
 
-         // possible.
 
-         this->updatePopup();
 
-         }
 
-       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 &&
 
-           obj != d->PopupPixmapWidget &&
 
-           qobject_cast<QWidget*>(obj)->windowType() == Qt::Popup)
 
-         {
 
-         break;
 
-         }
 
-       // The mouse might have left the area that keeps the popup open
 
-       QTimer::singleShot(d->HideDelay, this, SLOT(updatePopup()));
 
-       if (obj != d->BaseWidget &&
 
-           obj != d->PopupPixmapWidget)
 
-         {
 
-         obj->removeEventFilter(this);
 
-         }
 
-       break;
 
-     default:
 
-       break;
 
-     }
 
-   return this->QObject::eventFilter(obj, event);
 
- }
 
- // --------------------------------------------------------------------------
 
- void ctkPopupWidget::updatePopup()
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   // Querying mouseOver can be slow, don't do it if not needed.
 
-   QWidget* mouseOver = (d->AutoShow || d->AutoHide) ? d->mouseOver() : 0;
 
-   if ((d->AutoShow ||
 
-      // Even if there is no AutoShow, we might still want to reopen the popup
 
-      // when closing it inadvertently, except if we are un-pin-ing the popup
 
-       (d->AutoHide && d->isClosing() && this->property("AutoShowOnClose").toBool())) &&
 
-      // to be automatically open, the mouse has to be over a child widget
 
-       mouseOver &&
 
-      // disable opening the popup when the popup is disabled
 
-       (!d->BaseWidget || d->BaseWidget->isEnabled()))
 
-     {
 
-     this->showPopup();
 
-     }
 
-   else if (d->AutoHide && !mouseOver)
 
-     {
 
-     this->hidePopup();
 
-     }
 
- }
 
- // --------------------------------------------------------------------------
 
- void ctkPopupWidget::hidePopup()
 
- {
 
-   // just in case it was set.
 
-   this->setProperty("forcedClosed", 0);
 
-   this->Superclass::hidePopup();
 
- }
 
- // --------------------------------------------------------------------------
 
- void ctkPopupWidget::pinPopup(bool pin)
 
- {
 
-   Q_D(ctkPopupWidget);
 
-   this->setAutoHide(!pin);
 
-   if (pin)
 
-     {
 
-     this->showPopup();
 
-     }
 
-   else
 
-     {
 
-     // When closing, we don't want to inadvertently re-open the menu.
 
-     this->setProperty("AutoShowOnClose", this->autoShow());
 
-     d->AutoShow = false;
 
-     this->hidePopup();
 
-     }
 
- }
 
 
  |