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

Split ctkPopupWidget in 2 classes

ctkBasePopupWidget takes care of the display
ctkPopupWidget takes care of the events.
Julien Finet преди 14 години
родител
ревизия
9fce196f1d

+ 6 - 0
Libs/Widgets/CMakeLists.txt

@@ -15,6 +15,9 @@ SET(KIT_SRCS
   ctkAddRemoveComboBox.h
   ctkAxesWidget.cpp
   ctkAxesWidget.h
+  ctkBasePopupWidget.cpp
+  ctkBasePopupWidget.h
+  ctkBasePopupWidget_p.h
   ctkButtonGroup.cpp
   ctkButtonGroup.h
   ctkCheckableComboBox.cpp
@@ -89,6 +92,7 @@ SET(KIT_SRCS
   ctkPixmapIconEngine.h
   ctkPopupWidget.cpp
   ctkPopupWidget.h
+  ctkPopupWidget_p.h
   ctkQImageView.cpp
   ctkQImageView.h
   ctkRangeSlider.cpp
@@ -170,6 +174,8 @@ SET(KIT_MOC_SRCS
   ctkActionsWidget.h
   ctkAxesWidget.h
   ctkAddRemoveComboBox.h
+  ctkBasePopupWidget.h
+  ctkBasePopupWidget_p.h
   ctkButtonGroup.h
   ctkCheckableComboBox.h
   ctkCheckableHeaderView.h

+ 797 - 0
Libs/Widgets/ctkBasePopupWidget.cpp

@@ -0,0 +1,797 @@
+/*=========================================================================
+
+  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.commontk.org/LICENSE
+
+  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 "ctkBasePopupWidget_p.h"
+
+#define DEFAULT_FADING_DURATION 333 // fast enough without being too slow
+
+// -------------------------------------------------------------------------
+QGradient* duplicateGradient(const QGradient* gradient)
+{
+  QGradient* newGradient = 0;
+  switch (gradient->type())
+    {
+    case QGradient::LinearGradient:
+      {
+      const QLinearGradient* linearGradient = static_cast<const QLinearGradient*>(gradient);
+      newGradient = new QLinearGradient(linearGradient->start(), linearGradient->finalStop());
+      break;
+      }
+    case QGradient::RadialGradient:
+      {
+      const QRadialGradient* radialGradient = static_cast<const QRadialGradient*>(gradient);
+      newGradient = new QRadialGradient(radialGradient->center(), radialGradient->radius());
+      break;
+      }
+    case QGradient::ConicalGradient:
+      {
+      const QConicalGradient* conicalGradient = static_cast<const QConicalGradient*>(gradient);
+      newGradient = new QConicalGradient(conicalGradient->center(), conicalGradient->angle());
+      break;
+      }
+    default:
+      break;
+    }
+  if (!newGradient)
+    {
+    Q_ASSERT(gradient->type() != QGradient::NoGradient);
+    return newGradient;
+    }
+  newGradient->setCoordinateMode(gradient->coordinateMode());
+  newGradient->setSpread(gradient->spread());
+  newGradient->setStops(gradient->stops());
+  return newGradient;
+}
+
+// -------------------------------------------------------------------------
+ctkBasePopupWidgetPrivate::ctkBasePopupWidgetPrivate(ctkBasePopupWidget& object)
+  :q_ptr(&object)
+{
+  this->BaseWidget = 0;
+  this->Effect = ctkBasePopupWidget::ScrollEffect;
+  this->EffectAlpha = 1.;
+  this->AlphaAnimation = 0;
+  this->ForcedTranslucent = false;
+  this->ScrollAnimation = 0;
+  this->PopupPixmapWidget = 0;
+  // Geometry attributes
+  this->Alignment = Qt::AlignJustify | Qt::AlignBottom;
+  this->Orientations = Qt::Vertical;
+  this->VerticalDirection = ctkBasePopupWidget::TopToBottom;
+  this->HorizontalDirection = Qt::LeftToRight;
+}
+
+// -------------------------------------------------------------------------
+ctkBasePopupWidgetPrivate::~ctkBasePopupWidgetPrivate()
+{
+  delete this->PopupPixmapWidget;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidgetPrivate::init()
+{
+  Q_Q(ctkBasePopupWidget);
+  // By default, Tooltips are shown only on active windows. In a popup widget
+  // case, we sometimes aren't the active window but we still would like to
+  // show the children tooltips.
+  q->setAttribute(Qt::WA_AlwaysShowToolTips, true);
+
+  this->AlphaAnimation = new QPropertyAnimation(q, "effectAlpha", q);
+  this->AlphaAnimation->setDuration(DEFAULT_FADING_DURATION);
+  this->AlphaAnimation->setStartValue(0.);
+  this->AlphaAnimation->setEndValue(1.);
+  QObject::connect(this->AlphaAnimation, SIGNAL(finished()),
+                   q, SLOT(onEffectFinished()));
+
+  this->PopupPixmapWidget = new QLabel(0, Qt::ToolTip | Qt::FramelessWindowHint);
+
+  this->ScrollAnimation = new QPropertyAnimation(q, "effectGeometry", q);
+  this->ScrollAnimation->setDuration(DEFAULT_FADING_DURATION);
+  QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
+                   q, SLOT(onEffectFinished()));
+  QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
+                   this->PopupPixmapWidget, SLOT(hide()));
+
+  q->setAnimationEffect(this->Effect);
+  q->setEasingCurve(QEasingCurve::OutCubic);
+}
+
+// -------------------------------------------------------------------------
+QPropertyAnimation* ctkBasePopupWidgetPrivate::currentAnimation()const
+{
+  return this->Effect == ctkBasePopupWidget::ScrollEffect ?
+    this->ScrollAnimation : this->AlphaAnimation;
+}
+
+// -------------------------------------------------------------------------
+bool ctkBasePopupWidgetPrivate::isOpening()const
+{
+  return this->currentAnimation()->state() == QAbstractAnimation::Running &&
+    this->currentAnimation()->direction() == QAbstractAnimation::Forward;
+}
+
+// -------------------------------------------------------------------------
+bool ctkBasePopupWidgetPrivate::isClosing()const
+{
+  return this->currentAnimation()->state() == QAbstractAnimation::Running &&
+    this->currentAnimation()->direction() == QAbstractAnimation::Backward;
+}
+
+// -------------------------------------------------------------------------
+bool ctkBasePopupWidgetPrivate::wasClosing()const
+{
+  Q_Q(const ctkBasePopupWidget);
+  return qobject_cast<QAbstractAnimation*>(q->sender())->direction()
+    == QAbstractAnimation::Backward;
+}
+
+// -------------------------------------------------------------------------
+QWidgetList ctkBasePopupWidgetPrivate::focusWidgets(bool onlyVisible)const
+{
+  Q_Q(const ctkBasePopupWidget);
+  QWidgetList res;
+  if (!onlyVisible || q->isVisible())
+    {
+    res << const_cast<ctkBasePopupWidget*>(q);
+    }
+  if (this->BaseWidget && (!onlyVisible || this->BaseWidget->isVisible()))
+    {
+    res << this->BaseWidget;
+    }
+  if (this->PopupPixmapWidget && (!onlyVisible || this->PopupPixmapWidget->isVisible()))
+    {
+    res << this->PopupPixmapWidget;
+    }
+  return res;
+}
+
+// -------------------------------------------------------------------------
+QWidget* ctkBasePopupWidgetPrivate::mouseOver()
+{
+  Q_Q(ctkBasePopupWidget);
+  QList<QWidget*> widgets = this->focusWidgets(true);
+  foreach(QWidget* widget, widgets)
+    {
+    if (widget->underMouse())
+      {
+      return widget;
+      }
+    }
+  // Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
+  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 ||
+         QRect(QPoint(0,0), focusWidget->size()).contains(
+          focusWidget->mapFromGlobal(pos))))
+      {
+      return widgetUnderCursor;
+      }
+    }
+  return 0;
+}
+
+// -------------------------------------------------------------------------
+bool ctkBasePopupWidgetPrivate::isAncestorOf(const QWidget* ancestor, const QWidget* child)const
+{
+  while (child)
+    {
+    if (child == ancestor)
+        return true;
+    child = child->parentWidget();
+    }
+  return false;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidgetPrivate::setupPopupPixmapWidget()
+{
+  Q_Q(ctkBasePopupWidget);
+  this->PopupPixmapWidget->setAlignment(this->pixmapAlignment());  
+  QPixmap pixmap;
+  if (q->testAttribute(Qt::WA_TranslucentBackground))
+    {
+    // only QImage handle transparency correctly
+    QImage image(q->geometry().size(), QImage::Format_ARGB32);
+    image.fill(0);
+    q->render(&image);
+    pixmap = QPixmap::fromImage(image);
+    }
+  else
+    {
+    pixmap = QPixmap::grabWidget(q, QRect(QPoint(0,0), q->geometry().size()));
+    }
+  this->PopupPixmapWidget->setPixmap(pixmap);
+  this->PopupPixmapWidget->setAttribute(
+    Qt::WA_TranslucentBackground, q->testAttribute(Qt::WA_TranslucentBackground));
+  this->PopupPixmapWidget->setWindowOpacity(q->windowOpacity());
+}
+
+// -------------------------------------------------------------------------
+Qt::Alignment ctkBasePopupWidgetPrivate::pixmapAlignment()const
+{
+  Qt::Alignment alignment;
+  if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
+    {
+    alignment |= Qt::AlignBottom;
+    }
+  else// if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
+    {
+    alignment |= Qt::AlignTop;
+    }
+
+  if (this->HorizontalDirection == Qt::LeftToRight)
+    {
+    alignment |= Qt::AlignRight;
+    }
+  else// if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
+    {
+    alignment |= Qt::AlignLeft;
+    }
+  return alignment;
+}
+
+// -------------------------------------------------------------------------
+QRect ctkBasePopupWidgetPrivate::closedGeometry()const
+{
+  Q_Q(const ctkBasePopupWidget);
+  return this->closedGeometry(q->geometry());
+}
+
+// -------------------------------------------------------------------------
+QRect ctkBasePopupWidgetPrivate::closedGeometry(QRect openGeom)const
+{
+  if (this->Orientations & Qt::Vertical)
+    {
+    if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
+      {
+      openGeom.moveTop(openGeom.bottom());
+      }
+    openGeom.setHeight(0);
+    }
+  if (this->Orientations & Qt::Horizontal)
+    {
+    if (this->HorizontalDirection == Qt::RightToLeft)
+      {
+      openGeom.moveLeft(openGeom.right());
+      }
+    openGeom.setWidth(0);
+    }
+  return openGeom;
+}
+
+// -------------------------------------------------------------------------
+QRect ctkBasePopupWidgetPrivate::baseGeometry()const
+{
+  if (!this->BaseWidget)
+    {
+    return QRect();
+    }
+  return QRect(this->mapToGlobal(this->BaseWidget->geometry().topLeft()),
+               this->BaseWidget->size());
+}
+
+// -------------------------------------------------------------------------
+QPoint ctkBasePopupWidgetPrivate::mapToGlobal(const QPoint& baseWidgetPoint)const
+{
+  QPoint mappedPoint = baseWidgetPoint;
+  if (this->BaseWidget && this->BaseWidget->parentWidget())
+    {
+    mappedPoint = this->BaseWidget->parentWidget()->mapToGlobal(mappedPoint);
+    }
+  return mappedPoint;
+}
+
+// -------------------------------------------------------------------------
+QRect ctkBasePopupWidgetPrivate::desiredOpenGeometry()const
+{
+  return this->desiredOpenGeometry(this->baseGeometry());
+}
+
+// -------------------------------------------------------------------------
+QRect ctkBasePopupWidgetPrivate::desiredOpenGeometry(QRect baseGeometry)const
+{
+  Q_Q(const ctkBasePopupWidget);
+  QSize size = q->size();
+  if (!q->testAttribute(Qt::WA_WState_Created))
+    {
+    size = q->sizeHint();
+    }
+  
+  if (baseGeometry.isNull())
+    {
+    return QRect(q->pos(), size);
+    }
+
+  QRect geometry;
+  if (this->Alignment & Qt::AlignJustify)
+    {
+    if (this->Orientations & Qt::Vertical)
+      {
+      size.setWidth(baseGeometry.width());
+      }
+    }
+  if (this->Alignment & Qt::AlignTop &&
+      this->Alignment & Qt::AlignBottom)
+    {
+    size.setHeight(baseGeometry.height());
+    }
+
+  geometry.setSize(size);
+
+  QPoint topLeft = baseGeometry.topLeft();
+  QPoint bottomRight = baseGeometry.bottomRight();
+
+  if (this->Alignment & Qt::AlignLeft)
+    {
+    if (this->HorizontalDirection == Qt::LeftToRight)
+      {
+      geometry.moveLeft(topLeft.x());
+      }
+    else
+      {
+      geometry.moveRight(topLeft.x());
+      }
+    }
+  else if (this->Alignment & Qt::AlignRight)
+    {
+    if (this->HorizontalDirection == Qt::LeftToRight)
+      {
+      geometry.moveLeft(bottomRight.x());
+      }
+    else
+      {
+      geometry.moveRight(bottomRight.x());
+      }
+    }
+  else if (this->Alignment & Qt::AlignHCenter)
+    {
+    geometry.moveLeft((topLeft.x() + bottomRight.x()) / 2 - size.width() / 2);
+    }
+  else if (this->Alignment & Qt::AlignJustify)
+    {
+    geometry.moveLeft(topLeft.x());
+    }
+
+  if (this->Alignment & Qt::AlignTop)
+    {
+    if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
+      {
+      geometry.moveTop(topLeft.y());
+      }
+    else
+      {
+      geometry.moveBottom(topLeft.y());
+      }
+    }
+  else if (this->Alignment & Qt::AlignBottom)
+    {
+    if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
+      {
+      geometry.moveTop(bottomRight.y());
+      }
+    else
+      {
+      geometry.moveBottom(bottomRight.y());
+      }
+    }
+  else if (this->Alignment & Qt::AlignVCenter)
+    {
+    geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
+    }
+  return geometry;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidgetPrivate::hideAll()
+{
+  Q_Q(ctkBasePopupWidget);
+  // 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 ctkBasePopupWidget children, then hide it
+  // as well so we don't have a popup open while the ctkBasePopupWidget 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)
+ctkBasePopupWidget::ctkBasePopupWidget(QWidget* parentWidget)
+  : Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
+               Qt::ToolTip | Qt::FramelessWindowHint)
+  , d_ptr(new ctkBasePopupWidgetPrivate(*this))
+{
+  Q_D(ctkBasePopupWidget);
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkBasePopupWidget::ctkBasePopupWidget(ctkBasePopupWidgetPrivate* pimpl, QWidget* parentWidget)
+  : Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
+               Qt::ToolTip | Qt::FramelessWindowHint)
+  , d_ptr(pimpl)
+{
+}
+
+
+// -------------------------------------------------------------------------
+ctkBasePopupWidget::~ctkBasePopupWidget()
+{
+}
+
+// -------------------------------------------------------------------------
+QWidget* ctkBasePopupWidget::baseWidget()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->BaseWidget;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setBaseWidget(QWidget* widget)
+{
+  Q_D(ctkBasePopupWidget);
+  if (d->BaseWidget)
+    {
+    disconnect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
+               this, SLOT(onBaseWidgetDestroyed()));
+    }
+  d->BaseWidget = widget;
+  if (d->BaseWidget)
+    {
+    connect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
+            this, SLOT(onBaseWidgetDestroyed()));
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::onBaseWidgetDestroyed()
+{
+  Q_D(ctkBasePopupWidget);
+  d->hideAll();
+  this->setBaseWidget(0);
+  // could be a property.
+  this->deleteLater();
+}
+
+// -------------------------------------------------------------------------
+ctkBasePopupWidget::AnimationEffect ctkBasePopupWidget::animationEffect()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->Effect;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setAnimationEffect(ctkBasePopupWidget::AnimationEffect effect)
+{
+  Q_D(ctkBasePopupWidget);
+  /// TODO: handle the case where there is an animation running
+  d->Effect = effect;
+}
+
+// -------------------------------------------------------------------------
+QEasingCurve::Type ctkBasePopupWidget::easingCurve()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->AlphaAnimation->easingCurve().type();
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setEasingCurve(QEasingCurve::Type easingCurve)
+{
+  Q_D(ctkBasePopupWidget);
+  d->AlphaAnimation->setEasingCurve(easingCurve);
+  d->ScrollAnimation->setEasingCurve(easingCurve);
+}
+
+// -------------------------------------------------------------------------
+Qt::Alignment ctkBasePopupWidget::alignment()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->Alignment;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setAlignment(Qt::Alignment alignment)
+{
+  Q_D(ctkBasePopupWidget);
+  d->Alignment = alignment;
+}
+
+// -------------------------------------------------------------------------
+Qt::Orientations ctkBasePopupWidget::orientation()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->Orientations;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setOrientation(Qt::Orientations orientations)
+{
+  Q_D(ctkBasePopupWidget);
+  d->Orientations = orientations;
+}
+
+// -------------------------------------------------------------------------
+ctkBasePopupWidget::VerticalDirection ctkBasePopupWidget::verticalDirection()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->VerticalDirection;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setVerticalDirection(ctkBasePopupWidget::VerticalDirection verticalDirection)
+{
+  Q_D(ctkBasePopupWidget);
+  d->VerticalDirection = verticalDirection;
+}
+
+// -------------------------------------------------------------------------
+Qt::LayoutDirection ctkBasePopupWidget::horizontalDirection()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->HorizontalDirection;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::setHorizontalDirection(Qt::LayoutDirection horizontalDirection)
+{
+  Q_D(ctkBasePopupWidget);
+  d->HorizontalDirection = horizontalDirection;
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::onEffectFinished()
+{
+  Q_D(ctkBasePopupWidget);
+  if (d->ForcedTranslucent)
+    {
+    d->ForcedTranslucent = false;
+    this->setAttribute(Qt::WA_TranslucentBackground, false);
+    }
+  if (d->wasClosing())
+    {
+    d->hideAll();
+    emit this->popupOpened(false);
+    }
+  else
+    {
+    this->show();
+    emit this->popupOpened(true);
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::paintEvent(QPaintEvent* event)
+{
+  Q_D(ctkBasePopupWidget);
+  Q_UNUSED(event);
+
+  QPainter painter(this);
+  QBrush brush = this->palette().window();
+  if (brush.style() == Qt::LinearGradientPattern ||
+      brush.style() == Qt::ConicalGradientPattern ||
+      brush.style() == Qt::RadialGradientPattern)
+    {
+    QGradient* newGradient = duplicateGradient(brush.gradient());
+    QGradientStops stops;
+    foreach(QGradientStop stop, newGradient->stops())
+      {
+      stop.second.setAlpha(stop.second.alpha() * d->EffectAlpha);
+      stops.push_back(stop);
+      }
+    newGradient->setStops(stops);
+    brush = QBrush(*newGradient);
+    delete newGradient;
+    }
+  else
+    {
+    QColor color = brush.color();
+    color.setAlpha(color.alpha() * d->EffectAlpha);
+    brush.setColor(color);
+    }
+  //QColor semiTransparentColor = this->palette().window().color();
+  //semiTransparentColor.setAlpha(d->CurrentAlpha);
+  painter.fillRect(this->rect(), brush);
+  // Let the QFrame draw itself if needed
+  this->Superclass::paintEvent(event);
+}
+
+// --------------------------------------------------------------------------
+void ctkBasePopupWidget::showPopup()
+{
+  Q_D(ctkBasePopupWidget);
+  
+  if ((this->isVisible() &&
+       d->currentAnimation()->state() == QAbstractAnimation::Stopped) ||
+      (d->BaseWidget && !d->BaseWidget->isVisible()))
+    {
+    return;
+    }
+
+  // If the layout has never been activated, the widget doesn't know its
+  // minSize/maxSize and we then wouldn't know what's its true geometry.
+  if (this->layout() && !this->testAttribute(Qt::WA_WState_Created))
+    {
+    this->layout()->activate();
+    }
+  this->setGeometry(d->desiredOpenGeometry());
+  /// Maybe the popup doesn't allow the desiredOpenGeometry if the widget
+  /// minimum size is larger than the desired size.
+  QRect openGeometry = this->geometry();
+  QRect closedGeometry = d->closedGeometry();
+
+  d->currentAnimation()->setDirection(QAbstractAnimation::Forward);
+  
+  switch(d->Effect)
+    {
+    case WindowOpacityFadeEffect:
+      if (!this->testAttribute(Qt::WA_TranslucentBackground))
+        {
+        d->ForcedTranslucent = true;
+        this->setAttribute(Qt::WA_TranslucentBackground, true);
+        }
+      this->show();
+      break;
+    case ScrollEffect:
+      {
+      d->PopupPixmapWidget->setGeometry(closedGeometry);
+      d->ScrollAnimation->setStartValue(closedGeometry);
+      d->ScrollAnimation->setEndValue(openGeometry);
+      d->setupPopupPixmapWidget();
+      d->PopupPixmapWidget->show();
+      break;
+      }
+    default:
+      break;
+    }
+  switch(d->currentAnimation()->state())
+    {
+    case QAbstractAnimation::Stopped:
+      d->currentAnimation()->start();
+      break;
+    case QAbstractAnimation::Paused:
+      d->currentAnimation()->resume();
+      break;
+    default:
+    case QAbstractAnimation::Running:
+      break;
+    }
+}
+
+// --------------------------------------------------------------------------
+void ctkBasePopupWidget::hidePopup()
+{
+  Q_D(ctkBasePopupWidget);
+
+  if (!this->isVisible() &&
+      d->currentAnimation()->state() == QAbstractAnimation::Stopped)
+    {
+    return;
+    }
+  d->currentAnimation()->setDirection(QAbstractAnimation::Backward);
+
+  QRect openGeometry = this->geometry();
+  QRect closedGeometry = d->closedGeometry();
+
+  switch(d->Effect)
+    {
+    case WindowOpacityFadeEffect:
+      if (!this->testAttribute(Qt::WA_TranslucentBackground))
+        {
+        d->ForcedTranslucent = true;
+        this->setAttribute(Qt::WA_TranslucentBackground, true);
+        }
+      break;
+    case ScrollEffect:
+      {
+      d->ScrollAnimation->setStartValue(closedGeometry);
+      d->ScrollAnimation->setEndValue(openGeometry);
+      d->setupPopupPixmapWidget();
+      d->PopupPixmapWidget->setGeometry(this->geometry());
+      d->PopupPixmapWidget->show();
+      if (this->isActiveWindow())
+        {
+        qApp->setActiveWindow(d->BaseWidget ? d->BaseWidget->window() : 0);
+        }
+      this->hide();
+      break;
+      }
+    default:
+      break;
+    }
+  switch(d->currentAnimation()->state())
+    {
+    case QAbstractAnimation::Stopped:
+      d->currentAnimation()->start();
+      break;
+    case QAbstractAnimation::Paused:
+      d->currentAnimation()->resume();
+      break;
+    default:
+    case QAbstractAnimation::Running:
+      break;
+    }
+}
+
+// --------------------------------------------------------------------------
+double ctkBasePopupWidget::effectAlpha()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->EffectAlpha;
+}
+
+// --------------------------------------------------------------------------
+void ctkBasePopupWidget::setEffectAlpha(double alpha)
+{
+  Q_D(ctkBasePopupWidget);
+  d->EffectAlpha = alpha;
+  this->repaint();
+}
+
+// --------------------------------------------------------------------------
+QRect ctkBasePopupWidget::effectGeometry()const
+{
+  Q_D(const ctkBasePopupWidget);
+  return d->PopupPixmapWidget->geometry();
+}
+
+// --------------------------------------------------------------------------
+void ctkBasePopupWidget::setEffectGeometry(QRect newGeometry)
+{
+  Q_D(ctkBasePopupWidget);
+  d->PopupPixmapWidget->setGeometry(newGeometry);
+  d->PopupPixmapWidget->repaint();
+}

+ 160 - 0
Libs/Widgets/ctkBasePopupWidget.h

@@ -0,0 +1,160 @@
+/*=========================================================================
+
+  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.commontk.org/LICENSE
+
+  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.
+
+=========================================================================*/
+
+#ifndef __ctkBasePopupWidget_h
+#define __ctkBasePopupWidget_h
+
+// Qt includes
+#include <QEasingCurve>
+#include <QFrame>
+#include <QMetaType>
+
+// CTK includes
+#include "ctkWidgetsExport.h"
+
+class ctkBasePopupWidgetPrivate;
+
+/// Description:
+class CTK_WIDGETS_EXPORT ctkBasePopupWidget : public QFrame
+{
+  Q_OBJECT
+  
+  Q_ENUMS(AnimationEffect)
+  Q_ENUMS(VerticalDirection)
+
+  /// ScrollEffect by default
+  Q_PROPERTY( AnimationEffect animationEffect READ animationEffect WRITE setAnimationEffect)
+  
+  /// Opening/Closing curve
+  /// QEasingCurve::InOutQuad by default
+  Q_PROPERTY( QEasingCurve::Type easingCurve READ easingCurve WRITE setEasingCurve);
+  
+  /// Where is the popup in relation to the BaseWidget
+  /// To vertically justify, use Qt::AlignTop | Qt::AlignBottom
+  /// Qt::AlignJustify | Qt::AlignBottom by default
+  Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment);
+  
+  /// Direction of the scrolling effect, can be Qt::Vertical, Qt::Horizontal or
+  /// both Qt::Vertical|Qt::Horizontal.
+  /// Vertical by default
+  Q_PROPERTY( Qt::Orientations orientation READ orientation WRITE setOrientation);
+  
+  /// Control where the popup opens vertically.
+  /// TopToBottom by default
+  Q_PROPERTY( ctkBasePopupWidget::VerticalDirection verticalDirection READ verticalDirection WRITE setVerticalDirection);
+
+  /// Control where the popup opens horizontally.
+  /// LeftToRight by default
+  Q_PROPERTY( Qt::LayoutDirection horizontalDirection READ horizontalDirection WRITE setHorizontalDirection);
+
+public:
+  typedef QFrame Superclass;
+  explicit ctkBasePopupWidget(QWidget* parent = 0);
+  virtual ~ctkBasePopupWidget();
+
+  /// Widget the popup is attached to. It opens right under \a baseWidget
+  /// and if the ctkBasePopupWidget sizepolicy contains the growFlag/shrinkFlag,
+  /// it tries to resize itself to fit the same width of \a baseWidget.
+  QWidget* baseWidget()const;
+  virtual void setBaseWidget(QWidget* baseWidget);
+
+  enum AnimationEffect
+  {
+    WindowOpacityFadeEffect = 0,
+    ScrollEffect,
+    FadeEffect
+  };
+  
+  AnimationEffect animationEffect()const;
+  void setAnimationEffect(AnimationEffect effect);
+  
+  QEasingCurve::Type easingCurve()const;
+  void setEasingCurve(QEasingCurve::Type easingCurve);
+  
+  Qt::Alignment alignment()const;
+  void setAlignment(Qt::Alignment alignment);
+  
+  Qt::Orientations orientation()const;
+  void setOrientation(Qt::Orientations orientation);
+  
+  enum VerticalDirection{
+    TopToBottom = 1,
+    BottomToTop = 2
+  };
+  
+  VerticalDirection verticalDirection()const;
+  void setVerticalDirection(VerticalDirection direction);
+  
+  Qt::LayoutDirection horizontalDirection()const;
+  void setHorizontalDirection(Qt::LayoutDirection direction);
+
+public slots:
+  /// Hide the popup if open or opening. It takes around 300ms 
+  /// for the fading effect to hide the popup.
+  virtual void hidePopup();
+  /// Open the popup if closed or closing. It takes around 300ms 
+  /// for the fading effect to open the popup.
+  virtual void showPopup();
+  /// Show/hide the popup. It can be conveniently linked to a QPushButton
+  /// signal.
+  inline void showPopup(bool show);
+
+signals:
+  void popupOpened(bool open);
+
+protected:
+  explicit ctkBasePopupWidget(ctkBasePopupWidgetPrivate* pimpl, QWidget* parent = 0);
+  QScopedPointer<ctkBasePopupWidgetPrivate> d_ptr;
+  Q_PROPERTY(double effectAlpha READ effectAlpha WRITE setEffectAlpha DESIGNABLE false)
+  Q_PROPERTY(QRect effectGeometry READ effectGeometry WRITE setEffectGeometry DESIGNABLE false)
+
+  double effectAlpha()const;
+  QRect effectGeometry()const;
+
+  virtual void paintEvent(QPaintEvent*);
+
+protected slots:
+  virtual void onEffectFinished();
+  void setEffectAlpha(double alpha);
+  void setEffectGeometry(QRect geometry);
+  void onBaseWidgetDestroyed();
+
+private:
+  Q_DECLARE_PRIVATE(ctkBasePopupWidget);
+  Q_DISABLE_COPY(ctkBasePopupWidget);
+};
+
+Q_DECLARE_METATYPE(ctkBasePopupWidget::AnimationEffect)
+Q_DECLARE_METATYPE(ctkBasePopupWidget::VerticalDirection)
+
+// -------------------------------------------------------------------------
+void ctkBasePopupWidget::showPopup(bool show)
+{
+  if (show)
+    {
+    this->showPopup();
+    }
+  else
+    {
+    this->hidePopup();
+    }
+}
+
+#endif

+ 104 - 0
Libs/Widgets/ctkBasePopupWidget_p.h

@@ -0,0 +1,104 @@
+/*=========================================================================
+
+  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.
+
+=========================================================================*/
+
+#ifndef __ctkBasePopupWidget_p_h
+#define __ctkBasePopupWidget_p_h
+
+// Qt includes
+class QPropertyAnimation;
+class QLabel;
+
+// CTK includes
+#include "ctkBasePopupWidget.h"
+
+// -------------------------------------------------------------------------
+class CTK_WIDGETS_EXPORT ctkBasePopupWidgetPrivate
+  : public QObject
+{
+  Q_OBJECT
+  Q_DECLARE_PUBLIC(ctkBasePopupWidget);
+protected:
+  ctkBasePopupWidget* const q_ptr;
+public:
+  ctkBasePopupWidgetPrivate(ctkBasePopupWidget& object);
+  ~ctkBasePopupWidgetPrivate();
+  virtual void init();
+
+  bool isOpening()const;
+  bool isClosing()const;
+  /// Return true if the animation was closing (direction == backward).
+  /// It doesn't indicate if the action is still running or finished.
+  /// Can only be called in a slot as it uses sender().
+  bool wasClosing()const;
+
+  bool fitBaseWidgetSize()const;
+  Qt::Alignment pixmapAlignment()const;
+  void setupPopupPixmapWidget();
+
+  QWidgetList focusWidgets(bool onlyVisible = false)const;
+
+  // Return the widget if the mouse cursor is above any of the focus widgets or their
+  // children.
+  virtual QWidget* mouseOver();
+
+  // Same as QWidget::isAncestorOf() but don't restrain to the same window
+  // and apply it to all the focusWidgets
+  bool isAncestorOf(const QWidget* ancestor, const QWidget* child)const;
+
+
+  /// Return the closed geometry for the popup based on the current geometry
+  QRect closedGeometry()const;
+  /// Return the closed geometry for a given open geometry 
+  QRect closedGeometry(QRect openGeom)const;
+  
+  /// Return the desired geometry, maybe it won't happen if the size is too
+  /// small for the popup.
+  QRect desiredOpenGeometry()const;
+  QRect desiredOpenGeometry(QRect baseGeometry)const;
+  QRect baseGeometry()const;
+  QPoint mapToGlobal(const QPoint& baseWidgetPoint)const;
+  
+  QPropertyAnimation* currentAnimation()const;
+
+  //void temporarilyHiddenOn();
+  //void temporarilyHiddenOff();
+
+  void hideAll();
+
+protected:
+  QWidget* BaseWidget;
+
+  double EffectAlpha;
+
+  ctkBasePopupWidget::AnimationEffect Effect;
+  QPropertyAnimation* AlphaAnimation;
+  bool                ForcedTranslucent;
+  QPropertyAnimation* ScrollAnimation;
+  QLabel*             PopupPixmapWidget;
+  
+  // Geometry attributes
+  Qt::Alignment    Alignment;
+  Qt::Orientations Orientations;
+  
+  ctkBasePopupWidget::VerticalDirection VerticalDirection;
+  Qt::LayoutDirection HorizontalDirection;
+};
+
+#endif

+ 16 - 686
Libs/Widgets/ctkPopupWidget.cpp

@@ -42,380 +42,39 @@
 #define DEFAULT_FADING_DURATION 333 // fast enough without being too slow
 
 // -------------------------------------------------------------------------
-QGradient* duplicateGradient(const QGradient* gradient)
-{
-  QGradient* newGradient = 0;
-  switch (gradient->type())
-    {
-    case QGradient::LinearGradient:
-      {
-      const QLinearGradient* linearGradient = static_cast<const QLinearGradient*>(gradient);
-      newGradient = new QLinearGradient(linearGradient->start(), linearGradient->finalStop());
-      break;
-      }
-    case QGradient::RadialGradient:
-      {
-      const QRadialGradient* radialGradient = static_cast<const QRadialGradient*>(gradient);
-      newGradient = new QRadialGradient(radialGradient->center(), radialGradient->radius());
-      break;
-      }
-    case QGradient::ConicalGradient:
-      {
-      const QConicalGradient* conicalGradient = static_cast<const QConicalGradient*>(gradient);
-      newGradient = new QConicalGradient(conicalGradient->center(), conicalGradient->angle());
-      break;
-      }
-    default:
-      break;
-    }
-  if (!newGradient)
-    {
-    Q_ASSERT(gradient->type() != QGradient::NoGradient);
-    return newGradient;
-    }
-  newGradient->setCoordinateMode(gradient->coordinateMode());
-  newGradient->setSpread(gradient->spread());
-  newGradient->setStops(gradient->stops());
-  return newGradient;
-}
-
-// -------------------------------------------------------------------------
 ctkPopupWidgetPrivate::ctkPopupWidgetPrivate(ctkPopupWidget& object)
-  :q_ptr(&object)
+  :Superclass(object)
 {
-  this->BaseWidget = 0;
   this->AutoShow = true;
   this->AutoHide = true;
-  this->Effect = ctkPopupWidget::ScrollEffect;
-  this->EffectAlpha = 1.;
-  this->AlphaAnimation = 0;
-  this->ForcedTranslucent = false;
-  this->ScrollAnimation = 0;
-  this->PopupPixmapWidget = 0;
-  // Geometry attributes
-  this->Alignment = Qt::AlignJustify | Qt::AlignBottom;
-  this->Orientations = Qt::Vertical;
-  this->VerticalDirection = ctkPopupWidget::TopToBottom;
-  this->HorizontalDirection = Qt::LeftToRight;
 }
 
 // -------------------------------------------------------------------------
 ctkPopupWidgetPrivate::~ctkPopupWidgetPrivate()
 {
-  delete this->PopupPixmapWidget;
 }
 
 // -------------------------------------------------------------------------
 void ctkPopupWidgetPrivate::init()
 {
   Q_Q(ctkPopupWidget);
-  // By default, Tooltips are shown only on active windows. In a popup widget
-  // case, we sometimes aren't the active window but we still would like to
-  // show the children tooltips.
-  q->setAttribute(Qt::WA_AlwaysShowToolTips, true);
-
-  this->AlphaAnimation = new QPropertyAnimation(q, "effectAlpha", q);
-  this->AlphaAnimation->setDuration(DEFAULT_FADING_DURATION);
-  this->AlphaAnimation->setStartValue(0.);
-  this->AlphaAnimation->setEndValue(1.);
-  QObject::connect(this->AlphaAnimation, SIGNAL(finished()),
-                   q, SLOT(onEffectFinished()));
-
-  this->PopupPixmapWidget = new QLabel(0, Qt::ToolTip | Qt::FramelessWindowHint);
+  this->setParent(q);
+  this->Superclass::init();
   this->PopupPixmapWidget->installEventFilter(q);
-  this->ScrollAnimation = new QPropertyAnimation(q, "effectGeometry", q);
-  this->ScrollAnimation->setDuration(DEFAULT_FADING_DURATION);
-  QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
-                   q, SLOT(onEffectFinished()));
-
   qApp->installEventFilter(this);
-
-  q->setAnimationEffect(this->Effect);
-  q->setEasingCurve(QEasingCurve::OutCubic);
-}
-
-// -------------------------------------------------------------------------
-QPropertyAnimation* ctkPopupWidgetPrivate::currentAnimation()const
-{
-  return this->Effect == ctkPopupWidget::ScrollEffect ?
-    this->ScrollAnimation : this->AlphaAnimation;
-}
-
-// -------------------------------------------------------------------------
-bool ctkPopupWidgetPrivate::isOpening()const
-{
-  return this->currentAnimation()->state() == QAbstractAnimation::Running &&
-    this->currentAnimation()->direction() == QAbstractAnimation::Forward;
-}
-
-// -------------------------------------------------------------------------
-bool ctkPopupWidgetPrivate::isClosing()const
-{
-  return this->currentAnimation()->state() == QAbstractAnimation::Running &&
-    this->currentAnimation()->direction() == QAbstractAnimation::Backward;
-}
-
-// -------------------------------------------------------------------------
-QList<const QWidget*> ctkPopupWidgetPrivate::focusWidgets(bool onlyVisible)const
-{
-  Q_Q(const ctkPopupWidget);
-  QList<const QWidget*> res;
-  if (!onlyVisible || q->isVisible())
-    {
-    res << q;
-    }
-  if (this->BaseWidget && (!onlyVisible || this->BaseWidget->isVisible()))
-    {
-    res << this->BaseWidget;
-    }
-  if (this->PopupPixmapWidget && (!onlyVisible || this->PopupPixmapWidget->isVisible()))
-    {
-    res << this->PopupPixmapWidget;
-    }
-  return res;
-}
-
-// -------------------------------------------------------------------------
-bool ctkPopupWidgetPrivate::mouseOver()
-{
-  Q_Q(ctkPopupWidget);
-  QList<const QWidget*> widgets = this->focusWidgets(true);
-  foreach(const QWidget* widget, widgets)
-    {
-    if (widget->underMouse())
-      {
-      return true;
-      }
-    }
-  // Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
-  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 ||
-         QRect(QPoint(0,0), focusWidget->size()).contains(
-          focusWidget->mapFromGlobal(pos))))
-      {
-      widgetUnderCursor->installEventFilter(q);
-      return true;
-      }
-    }
-  return false;
-}
-
-// -------------------------------------------------------------------------
-bool ctkPopupWidgetPrivate::isAncestorOf(const QWidget* ancestor, const QWidget* child)const
-{
-  while (child)
-    {
-    if (child == ancestor)
-        return true;
-    child = child->parentWidget();
-    }
-  return false;
 }
 
 // -------------------------------------------------------------------------
-void ctkPopupWidgetPrivate::setupPopupPixmapWidget()
+QWidget* ctkPopupWidgetPrivate::mouseOver()
 {
   Q_Q(ctkPopupWidget);
-  this->PopupPixmapWidget->setAlignment(this->pixmapAlignment());  
-  QPixmap pixmap;
-  if (q->testAttribute(Qt::WA_TranslucentBackground))
-    {
-    // only QImage handle transparency correctly
-    QImage image(q->geometry().size(), QImage::Format_ARGB32);
-    image.fill(0);
-    q->render(&image);
-    pixmap = QPixmap::fromImage(image);
-    }
-  else
+  QWidget* widgetUnderCursor = this->Superclass::mouseOver();
+  if (widgetUnderCursor &&
+      !this->focusWidgets(true).contains(widgetUnderCursor))
     {
-    pixmap = QPixmap::grabWidget(q, QRect(QPoint(0,0), q->geometry().size()));
+    widgetUnderCursor->installEventFilter(q);
     }
-  this->PopupPixmapWidget->setPixmap(pixmap);
-  this->PopupPixmapWidget->setAttribute(
-    Qt::WA_TranslucentBackground, q->testAttribute(Qt::WA_TranslucentBackground));
-  this->PopupPixmapWidget->setWindowOpacity(q->windowOpacity());
-}
-
-// -------------------------------------------------------------------------
-Qt::Alignment ctkPopupWidgetPrivate::pixmapAlignment()const
-{
-  Qt::Alignment alignment;
-  if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
-    {
-    alignment |= Qt::AlignBottom;
-    }
-  else// if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
-    {
-    alignment |= Qt::AlignTop;
-    }
-
-  if (this->HorizontalDirection == Qt::LeftToRight)
-    {
-    alignment |= Qt::AlignRight;
-    }
-  else// if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
-    {
-    alignment |= Qt::AlignLeft;
-    }
-  return alignment;
-}
-
-// -------------------------------------------------------------------------
-QRect ctkPopupWidgetPrivate::closedGeometry()const
-{
-  Q_Q(const ctkPopupWidget);
-  return this->closedGeometry(q->geometry());
-}
-
-// -------------------------------------------------------------------------
-QRect ctkPopupWidgetPrivate::closedGeometry(QRect openGeom)const
-{
-  if (this->Orientations & Qt::Vertical)
-    {
-    if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
-      {
-      openGeom.moveTop(openGeom.bottom());
-      }
-    openGeom.setHeight(0);
-    }
-  if (this->Orientations & Qt::Horizontal)
-    {
-    if (this->HorizontalDirection == Qt::RightToLeft)
-      {
-      openGeom.moveLeft(openGeom.right());
-      }
-    openGeom.setWidth(0);
-    }
-  return openGeom;
-}
-
-// -------------------------------------------------------------------------
-QRect ctkPopupWidgetPrivate::baseGeometry()const
-{
-  if (!this->BaseWidget)
-    {
-    return QRect();
-    }
-  return QRect(this->mapToGlobal(this->BaseWidget->geometry().topLeft()),
-               this->BaseWidget->size());
-}
-
-// -------------------------------------------------------------------------
-QPoint ctkPopupWidgetPrivate::mapToGlobal(const QPoint& baseWidgetPoint)const
-{
-  QPoint mappedPoint = baseWidgetPoint;
-  if (this->BaseWidget && this->BaseWidget->parentWidget())
-    {
-    mappedPoint = this->BaseWidget->parentWidget()->mapToGlobal(mappedPoint);
-    }
-  return mappedPoint;
-}
-
-// -------------------------------------------------------------------------
-QRect ctkPopupWidgetPrivate::desiredOpenGeometry()const
-{
-  return this->desiredOpenGeometry(this->baseGeometry());
-}
-
-// -------------------------------------------------------------------------
-QRect ctkPopupWidgetPrivate::desiredOpenGeometry(QRect baseGeometry)const
-{
-  Q_Q(const ctkPopupWidget);
-  QSize size = q->size();
-  if (!q->testAttribute(Qt::WA_WState_Created))
-    {
-    size = q->sizeHint();
-    }
-  
-  if (baseGeometry.isNull())
-    {
-    return QRect(q->pos(), size);
-    }
-
-  QRect geometry;
-  if (this->Alignment & Qt::AlignJustify)
-    {
-    if (this->Orientations & Qt::Vertical)
-      {
-      size.setWidth(baseGeometry.width());
-      }
-    }
-  if (this->Alignment & Qt::AlignTop &&
-      this->Alignment & Qt::AlignBottom)
-    {
-    size.setHeight(baseGeometry.height());
-    }
-
-  geometry.setSize(size);
-
-  QPoint topLeft = baseGeometry.topLeft();
-  QPoint bottomRight = baseGeometry.bottomRight();
-
-  if (this->Alignment & Qt::AlignLeft)
-    {
-    if (this->HorizontalDirection == Qt::LeftToRight)
-      {
-      geometry.moveLeft(topLeft.x());
-      }
-    else
-      {
-      geometry.moveRight(topLeft.x());
-      }
-    }
-  else if (this->Alignment & Qt::AlignRight)
-    {
-    if (this->HorizontalDirection == Qt::LeftToRight)
-      {
-      geometry.moveLeft(bottomRight.x());
-      }
-    else
-      {
-      geometry.moveRight(bottomRight.x());
-      }
-    }
-  else if (this->Alignment & Qt::AlignHCenter)
-    {
-    geometry.moveLeft((topLeft.x() + bottomRight.x()) / 2 - size.width() / 2);
-    }
-  else if (this->Alignment & Qt::AlignJustify)
-    {
-    geometry.moveLeft(topLeft.x());
-    }
-
-  if (this->Alignment & Qt::AlignTop)
-    {
-    if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
-      {
-      geometry.moveTop(topLeft.y());
-      }
-    else
-      {
-      geometry.moveBottom(topLeft.y());
-      }
-    }
-  else if (this->Alignment & Qt::AlignBottom)
-    {
-    if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
-      {
-      geometry.moveTop(bottomRight.y());
-      }
-    else
-      {
-      geometry.moveBottom(bottomRight.y());
-      }
-    }
-  else if (this->Alignment & Qt::AlignVCenter)
-    {
-    geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
-    }
-  return geometry;
+  return widgetUnderCursor;
 }
 
 // -------------------------------------------------------------------------
@@ -548,16 +207,6 @@ void ctkPopupWidgetPrivate::updateVisibility()
 }
 
 // -------------------------------------------------------------------------
-void ctkPopupWidgetPrivate::onBaseWidgetDestroyed()
-{
-  Q_Q(ctkPopupWidget);
-  this->hideAll();
-  q->setBaseWidget(0);
-  // could be a property.
-  q->deleteLater();
-}
-
-// -------------------------------------------------------------------------
 void ctkPopupWidgetPrivate::temporarilyHiddenOn()
 {
   Q_Q(ctkPopupWidget);
@@ -593,37 +242,11 @@ 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)
 ctkPopupWidget::ctkPopupWidget(QWidget* parentWidget)
-  : Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
-               Qt::ToolTip | Qt::FramelessWindowHint)
-  , d_ptr(new ctkPopupWidgetPrivate(*this))
+  : Superclass(new ctkPopupWidgetPrivate(*this), parentWidget)
 {
   Q_D(ctkPopupWidget);
   d->init();
@@ -635,28 +258,17 @@ ctkPopupWidget::~ctkPopupWidget()
 }
 
 // -------------------------------------------------------------------------
-QWidget* ctkPopupWidget::baseWidget()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->BaseWidget;
-}
-
-// -------------------------------------------------------------------------
 void ctkPopupWidget::setBaseWidget(QWidget* widget)
 {
   Q_D(ctkPopupWidget);
   if (d->BaseWidget)
     {
     d->BaseWidget->removeEventFilter(this);
-    disconnect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
-               d, SLOT(onBaseWidgetDestroyed()));
     }
-  d->BaseWidget = widget;
+  this->Superclass::setBaseWidget(widget);
   if (d->BaseWidget)
     {
     d->BaseWidget->installEventFilter(this);
-    connect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
-            d, SLOT(onBaseWidgetDestroyed()));
     }
   QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
 }
@@ -692,104 +304,13 @@ void ctkPopupWidget::setAutoHide(bool mode)
 }
 
 // -------------------------------------------------------------------------
-ctkPopupWidget::AnimationEffect ctkPopupWidget::animationEffect()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->Effect;
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::setAnimationEffect(ctkPopupWidget::AnimationEffect effect)
-{
-  Q_D(ctkPopupWidget);
-  /// TODO: handle the case where there is an animation running
-  d->Effect = effect;
-}
-
-// -------------------------------------------------------------------------
-QEasingCurve::Type ctkPopupWidget::easingCurve()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->AlphaAnimation->easingCurve().type();
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::setEasingCurve(QEasingCurve::Type easingCurve)
-{
-  Q_D(ctkPopupWidget);
-  d->AlphaAnimation->setEasingCurve(easingCurve);
-  d->ScrollAnimation->setEasingCurve(easingCurve);
-}
-
-// -------------------------------------------------------------------------
-Qt::Alignment ctkPopupWidget::alignment()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->Alignment;
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::setAlignment(Qt::Alignment alignment)
-{
-  Q_D(ctkPopupWidget);
-  d->Alignment = alignment;
-}
-
-// -------------------------------------------------------------------------
-Qt::Orientations ctkPopupWidget::orientation()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->Orientations;
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::setOrientation(Qt::Orientations orientations)
-{
-  Q_D(ctkPopupWidget);
-  d->Orientations = orientations;
-}
-
-// -------------------------------------------------------------------------
-ctkPopupWidget::VerticalDirection ctkPopupWidget::verticalDirection()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->VerticalDirection;
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::setVerticalDirection(ctkPopupWidget::VerticalDirection verticalDirection)
-{
-  Q_D(ctkPopupWidget);
-  d->VerticalDirection = verticalDirection;
-}
-
-// -------------------------------------------------------------------------
-Qt::LayoutDirection ctkPopupWidget::horizontalDirection()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->HorizontalDirection;
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::setHorizontalDirection(Qt::LayoutDirection horizontalDirection)
-{
-  Q_D(ctkPopupWidget);
-  d->HorizontalDirection = horizontalDirection;
-}
-
-// -------------------------------------------------------------------------
 void ctkPopupWidget::onEffectFinished()
 {
   Q_D(ctkPopupWidget);
-  if (d->ForcedTranslucent)
+  bool wasClosing = d->wasClosing();
+  this->Superclass::onEffectFinished();
+  if (wasClosing)
     {
-    d->ForcedTranslucent = false;
-    this->setAttribute(Qt::WA_TranslucentBackground, false);
-    }
-  if (qobject_cast<QAbstractAnimation*>(this->sender())->direction() == QAbstractAnimation::Backward)
-    {
-    d->hideAll();
-    emit this->popupOpened(false);
     /// restore the AutoShow if needed.
     if (!this->property("AutoShowOnClose").isNull())
       {
@@ -797,59 +318,6 @@ void ctkPopupWidget::onEffectFinished()
       this->setProperty("AutoShowOnClose", QVariant());
       }
     }
-  else
-    {
-    this->show();
-#ifdef Q_WS_X11
-    // If the OS applies effects on window appearance, it
-    // can take time for the popup to be displayed, we don't want to
-    // hide the pixmap too early otherwise to would makes it "flicker"
-    // It has the disadvantage of 'opaquing' if the popup is
-    // semi transparent because the pixmap and the popup opacities
-    // get summed up until the pixmap is hidden.
-    // Alternatively, you could remove effects on windows with Compiz.
-    QTimer::singleShot(100, d->PopupPixmapWidget, SLOT(hide()));
-#else
-    d->PopupPixmapWidget->hide();
-#endif
-    emit this->popupOpened(true);
-    }
-}
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::paintEvent(QPaintEvent* event)
-{
-  Q_D(ctkPopupWidget);
-  Q_UNUSED(event);
-
-  QPainter painter(this);
-  QBrush brush = this->palette().window();
-  if (brush.style() == Qt::LinearGradientPattern ||
-      brush.style() == Qt::ConicalGradientPattern ||
-      brush.style() == Qt::RadialGradientPattern)
-    {
-    QGradient* newGradient = duplicateGradient(brush.gradient());
-    QGradientStops stops;
-    foreach(QGradientStop stop, newGradient->stops())
-      {
-      stop.second.setAlpha(stop.second.alpha() * d->EffectAlpha);
-      stops.push_back(stop);
-      }
-    newGradient->setStops(stops);
-    brush = QBrush(*newGradient);
-    delete newGradient;
-    }
-  else
-    {
-    QColor color = brush.color();
-    color.setAlpha(color.alpha() * d->EffectAlpha);
-    brush.setColor(color);
-    }
-  //QColor semiTransparentColor = this->palette().window().color();
-  //semiTransparentColor.setAlpha(d->CurrentAlpha);
-  painter.fillRect(this->rect(), brush);
-  // Let the QFrame draw itself if needed
-  this->Superclass::paintEvent(event);
 }
 
 // --------------------------------------------------------------------------
@@ -967,7 +435,7 @@ void ctkPopupWidget::updatePopup()
   Q_D(ctkPopupWidget);
 
   // Querying mouseOver can be slow, don't do it if not needed.
-  bool mouseOver = (d->AutoShow || d->AutoHide) && d->mouseOver();
+  QWidget* mouseOver = (d->AutoShow || d->AutoHide) ? d->mouseOver() : 0;
   if (d->AutoShow && mouseOver)
     {
     this->showPopup();
@@ -978,67 +446,6 @@ void ctkPopupWidget::updatePopup()
     }
 }
 
-// --------------------------------------------------------------------------
-void ctkPopupWidget::showPopup()
-{
-  Q_D(ctkPopupWidget);
-  
-  if ((this->isVisible() &&
-       d->currentAnimation()->state() == QAbstractAnimation::Stopped) ||
-      (d->BaseWidget && !d->BaseWidget->isVisible()))
-    {
-    return;
-    }
-
-  // If the layout has never been activated, the widget doesn't know its
-  // minSize/maxSize and we then wouldn't know what's its true geometry.
-  if (this->layout() && !this->testAttribute(Qt::WA_WState_Created))
-    {
-    this->layout()->activate();
-    }
-  this->setGeometry(d->desiredOpenGeometry());
-  /// Maybe the popup doesn't allow the desiredOpenGeometry if the widget
-  /// minimum size is larger than the desired size.
-  QRect openGeometry = this->geometry();
-  QRect closedGeometry = d->closedGeometry();
-
-  d->currentAnimation()->setDirection(QAbstractAnimation::Forward);
-  
-  switch(d->Effect)
-    {
-    case WindowOpacityFadeEffect:
-      if (!this->testAttribute(Qt::WA_TranslucentBackground))
-        {
-        d->ForcedTranslucent = true;
-        this->setAttribute(Qt::WA_TranslucentBackground, true);
-        }
-      this->show();
-      break;
-    case ScrollEffect:
-      {
-      d->PopupPixmapWidget->setGeometry(closedGeometry);
-      d->ScrollAnimation->setStartValue(closedGeometry);
-      d->ScrollAnimation->setEndValue(openGeometry);
-      d->setupPopupPixmapWidget();
-      d->PopupPixmapWidget->show();
-      break;
-      }
-    default:
-      break;
-    }
-  switch(d->currentAnimation()->state())
-    {
-    case QAbstractAnimation::Stopped:
-      d->currentAnimation()->start();
-      break;
-    case QAbstractAnimation::Paused:
-      d->currentAnimation()->resume();
-      break;
-    default:
-    case QAbstractAnimation::Running:
-      break;
-    }
-}
 
 // --------------------------------------------------------------------------
 void ctkPopupWidget::hidePopup()
@@ -1048,54 +455,7 @@ void ctkPopupWidget::hidePopup()
   // just in case it was set.
   this->setProperty("forcedClosed", 0);
 
-  if (!this->isVisible() &&
-      d->currentAnimation()->state() == QAbstractAnimation::Stopped)
-    {
-    return;
-    }
-  d->currentAnimation()->setDirection(QAbstractAnimation::Backward);
-
-  QRect openGeometry = this->geometry();
-  QRect closedGeometry = d->closedGeometry();
-
-  switch(d->Effect)
-    {
-    case WindowOpacityFadeEffect:
-      if (!this->testAttribute(Qt::WA_TranslucentBackground))
-        {
-        d->ForcedTranslucent = true;
-        this->setAttribute(Qt::WA_TranslucentBackground, true);
-        }
-      break;
-    case ScrollEffect:
-      {
-      d->ScrollAnimation->setStartValue(closedGeometry);
-      d->ScrollAnimation->setEndValue(openGeometry);
-      d->setupPopupPixmapWidget();
-      d->PopupPixmapWidget->setGeometry(this->geometry());
-      d->PopupPixmapWidget->show();
-      if (this->isActiveWindow())
-        {
-        qApp->setActiveWindow(d->BaseWidget ? d->BaseWidget->window() : 0);
-        }
-      this->hide();
-      break;
-      }
-    default:
-      break;
-    }
-  switch(d->currentAnimation()->state())
-    {
-    case QAbstractAnimation::Stopped:
-      d->currentAnimation()->start();
-      break;
-    case QAbstractAnimation::Paused:
-      d->currentAnimation()->resume();
-      break;
-    default:
-    case QAbstractAnimation::Running:
-      break;
-    }
+  this->Superclass::hidePopup();
 }
 
 // --------------------------------------------------------------------------
@@ -1115,33 +475,3 @@ void ctkPopupWidget::pinPopup(bool pin)
     this->hidePopup();
     }
 }
-
-// --------------------------------------------------------------------------
-double ctkPopupWidget::effectAlpha()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->EffectAlpha;
-}
-
-// --------------------------------------------------------------------------
-void ctkPopupWidget::setEffectAlpha(double alpha)
-{
-  Q_D(ctkPopupWidget);
-  d->EffectAlpha = alpha;
-  this->repaint();
-}
-
-// --------------------------------------------------------------------------
-QRect ctkPopupWidget::effectGeometry()const
-{
-  Q_D(const ctkPopupWidget);
-  return d->PopupPixmapWidget->geometry();
-}
-
-// --------------------------------------------------------------------------
-void ctkPopupWidget::setEffectGeometry(QRect newGeometry)
-{
-  Q_D(ctkPopupWidget);
-  d->PopupPixmapWidget->setGeometry(newGeometry);
-  d->PopupPixmapWidget->repaint();
-}

+ 8 - 107
Libs/Widgets/ctkPopupWidget.h

@@ -21,23 +21,15 @@
 #ifndef __ctkPopupWidget_h
 #define __ctkPopupWidget_h
 
-// Qt includes
-#include <QEasingCurve>
-#include <QFrame>
-#include <QMetaType>
-
 // CTK includes
-#include "ctkWidgetsExport.h"
+#include "ctkBasePopupWidget.h"
 
 class ctkPopupWidgetPrivate;
 
 /// Description:
-class CTK_WIDGETS_EXPORT ctkPopupWidget : public QFrame
+class CTK_WIDGETS_EXPORT ctkPopupWidget : public ctkBasePopupWidget
 {
   Q_OBJECT
-  
-  Q_ENUMS(AnimationEffect)
-  Q_ENUMS(VerticalDirection)
 
   /// Control wether the popup automatically opens when the mouse
   /// enter the widget. True by default
@@ -47,41 +39,15 @@ class CTK_WIDGETS_EXPORT ctkPopupWidget : public QFrame
   /// leaves the widget. True by default
   Q_PROPERTY( bool autoHide READ autoHide WRITE setAutoHide)
 
-  /// ScrollEffect by default
-  Q_PROPERTY( AnimationEffect animationEffect READ animationEffect WRITE setAnimationEffect)
-  
-  /// Opening/Closing curve
-  /// QEasingCurve::InOutQuad by default
-  Q_PROPERTY( QEasingCurve::Type easingCurve READ easingCurve WRITE setEasingCurve);
-  
-  /// Where is the popup in relation to the BaseWidget
-  /// To vertically justify, use Qt::AlignTop | Qt::AlignBottom
-  /// Qt::AlignJustify | Qt::AlignBottom by default
-  Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment);
-  
-  /// Direction of the scrolling effect, can be Qt::Vertical, Qt::Horizontal or
-  /// both Qt::Vertical|Qt::Horizontal.
-  /// Vertical by default
-  Q_PROPERTY( Qt::Orientations orientation READ orientation WRITE setOrientation);
-  
-  /// Control where the popup opens vertically.
-  /// TopToBottom by default
-  Q_PROPERTY( ctkPopupWidget::VerticalDirection verticalDirection READ verticalDirection WRITE setVerticalDirection);
-
-  /// Control where the popup opens horizontally.
-  /// LeftToRight by default
-  Q_PROPERTY( Qt::LayoutDirection horizontalDirection READ horizontalDirection WRITE setHorizontalDirection);
-
 public:
-  typedef QFrame Superclass;
+  typedef ctkBasePopupWidget Superclass;
   explicit ctkPopupWidget(QWidget* parent = 0);
   virtual ~ctkPopupWidget();
 
   /// Widget the popup is attached to. It opens right under \a baseWidget
   /// and if the ctkPopupWidget sizepolicy contains the growFlag/shrinkFlag,
   /// it tries to resize itself to fit the same width of \a baseWidget.
-  QWidget* baseWidget()const;
-  void setBaseWidget(QWidget* baseWidget);
+  virtual void setBaseWidget(QWidget* baseWidget);
 
   bool autoShow()const;
   /// Calling setAutoShow automatically updates opens the popup if the cursor
@@ -94,93 +60,28 @@ public:
   /// if the mouse is not over the popup nor the base widget.
   void setAutoHide(bool autoHide);
 
-  enum AnimationEffect
-  {
-    WindowOpacityFadeEffect = 0,
-    ScrollEffect,
-    FadeEffect
-  };
-  
-  AnimationEffect animationEffect()const;
-  void setAnimationEffect(AnimationEffect effect);
-  
-  QEasingCurve::Type easingCurve()const;
-  void setEasingCurve(QEasingCurve::Type easingCurve);
-  
-  Qt::Alignment alignment()const;
-  void setAlignment(Qt::Alignment alignment);
-  
-  Qt::Orientations orientation()const;
-  void setOrientation(Qt::Orientations orientation);
-  
-  enum VerticalDirection{
-    TopToBottom = 1,
-    BottomToTop = 2
-  };
-  
-  VerticalDirection verticalDirection()const;
-  void setVerticalDirection(VerticalDirection direction);
-  
-  Qt::LayoutDirection horizontalDirection()const;
-  void setHorizontalDirection(Qt::LayoutDirection direction);
-
 public slots:
-  /// Hide the popup if open or opening. It takes around 300ms 
-  /// for the fading effect to hide the popup.
-  void hidePopup();
-  /// Open the popup if closed or closing. It takes around 300ms 
-  /// for the fading effect to open the popup.
-  void showPopup();
-  /// Show/hide the popup. It can be conveniently linked to a QPushButton
-  /// signal.
-  inline void showPopup(bool show);
-  
   /// Convenient function that calls setAutoHide(!pin) and opens the popup
   /// if pin is true regardless of the value of \a AutoShow.
   /// It is typically connected with a checkable button to anchor the popup.
   void pinPopup(bool pin);
 
-signals:
-  void popupOpened(bool open);
+public:
+  /// Reimplemented for internal reasons
+  virtual void hidePopup();
 
 protected:
-  QScopedPointer<ctkPopupWidgetPrivate> d_ptr;
-  Q_PROPERTY(double effectAlpha READ effectAlpha WRITE setEffectAlpha DESIGNABLE false)
-  Q_PROPERTY(QRect effectGeometry READ effectGeometry WRITE setEffectGeometry DESIGNABLE false)
-
-  double effectAlpha()const;
-  QRect effectGeometry()const;
-
-  virtual void paintEvent(QPaintEvent*);
   virtual void leaveEvent(QEvent* event);
   virtual void enterEvent(QEvent* event);
   virtual bool eventFilter(QObject* obj, QEvent* event);
 
 protected slots:
   void updatePopup();
-  void onEffectFinished();
-  void setEffectAlpha(double alpha);
-  void setEffectGeometry(QRect geometry);
+  virtual void onEffectFinished();
 
 private:
   Q_DECLARE_PRIVATE(ctkPopupWidget);
   Q_DISABLE_COPY(ctkPopupWidget);
 };
 
-Q_DECLARE_METATYPE(ctkPopupWidget::AnimationEffect)
-Q_DECLARE_METATYPE(ctkPopupWidget::VerticalDirection)
-
-// -------------------------------------------------------------------------
-void ctkPopupWidget::showPopup(bool show)
-{
-  if (show)
-    {
-    this->showPopup();
-    }
-  else
-    {
-    this->hidePopup();
-    }
-}
-
 #endif

+ 8 - 57
Libs/Widgets/ctkPopupWidget_p.h

@@ -21,90 +21,41 @@
 #ifndef __ctkPopupWidget_p_h
 #define __ctkPopupWidget_p_h
 
-// Qt includes
-class QPropertyAnimation;
-class QLabel;
-
 // CTK includes
+#include "ctkBasePopupWidget_p.h"
 #include "ctkPopupWidget.h"
 
 // -------------------------------------------------------------------------
-class CTK_WIDGETS_EXPORT ctkPopupWidgetPrivate: public QObject
+class CTK_WIDGETS_EXPORT ctkPopupWidgetPrivate
+  : public ctkBasePopupWidgetPrivate
 {
   Q_OBJECT
   Q_DECLARE_PUBLIC(ctkPopupWidget);
-protected:
-  ctkPopupWidget* const q_ptr;
 public:
+  typedef ctkBasePopupWidgetPrivate Superclass;
   ctkPopupWidgetPrivate(ctkPopupWidget& object);
   ~ctkPopupWidgetPrivate();
-  void init();
-  
-  bool isOpening()const;
-  bool isClosing()const;
-
-  bool fitBaseWidgetSize()const;
-  Qt::Alignment pixmapAlignment()const;
-  void setupPopupPixmapWidget();
 
-  QList<const QWidget*> focusWidgets(bool onlyVisible = false)const;
+  virtual void init();
 
-  // Return true if the mouse cursor is above any of the focus widgets or their
+  // Return the widget if the mouse cursor is above any of the focus widgets or their
   // children.
   // If the cursor is above a child widget, install the event filter to listen
   // when the cursor leaves the widget.
-  bool mouseOver();
-
-  // Same as QWidget::isAncestorOf() but don't restrain to the same window
-  // and apply it to all the focusWidgets
-  bool isAncestorOf(const QWidget* ancestor, const QWidget* child)const;
-
-
-  /// Return the closed geometry for the popup based on the current geometry
-  QRect closedGeometry()const;
-  /// Return the closed geometry for a given open geometry 
-  QRect closedGeometry(QRect openGeom)const;
-  
-  /// Return the desired geometry, maybe it won't happen if the size is too
-  /// small for the popup.
-  QRect desiredOpenGeometry()const;
-  QRect desiredOpenGeometry(QRect baseGeometry)const;
-  QRect baseGeometry()const;
-  QPoint mapToGlobal(const QPoint& baseWidgetPoint)const;
-  
-  QPropertyAnimation* currentAnimation()const;
-  
+  virtual QWidget* mouseOver();
+
   virtual bool eventFilter(QObject* obj, QEvent* event);
 
   void temporarilyHiddenOn();
   void temporarilyHiddenOff();
 
-  void hideAll();
-
 public slots:
   void updateVisibility();
-  void onBaseWidgetDestroyed();
   void onApplicationDeactivate();
 
 protected:
-  QWidget* BaseWidget;
   bool AutoShow;
   bool AutoHide;
-
-  double EffectAlpha;
-
-  ctkPopupWidget::AnimationEffect Effect;
-  QPropertyAnimation* AlphaAnimation;
-  bool                ForcedTranslucent;
-  QPropertyAnimation* ScrollAnimation;
-  QLabel*             PopupPixmapWidget;
-  
-  // Geometry attributes
-  Qt::Alignment    Alignment;
-  Qt::Orientations Orientations;
-  
-  ctkPopupWidget::VerticalDirection VerticalDirection;
-  Qt::LayoutDirection HorizontalDirection;
 };
 
 #endif