Browse Source

Add Scroll effect to ctkPopupWidget

Julien Finet 13 years ago
parent
commit
a7e4d464a8

+ 78 - 86
Libs/Widgets/Testing/Cpp/ctkPopupWidgetTest1.cpp

@@ -34,10 +34,67 @@
 #include <iostream>
 
 //-----------------------------------------------------------------------------
-void changeOpacity(void* callData)
+QWidget* createPanel(const QString& title, QList<ctkPopupWidget*>& popups)
 {
-  ctkPopupWidget* popup = reinterpret_cast<ctkPopupWidget*>(callData);
-  popup->setOpacity(qobject_cast<QSlider*>(popup->baseWidget())->value());
+  QWidget* topLevel = new QWidget(0);
+  topLevel->setWindowTitle(title);
+  
+  QPushButton* focusButton = new QPushButton("Focus popup");
+  QPushButton* openButton = new QPushButton("Open popup");
+  QPushButton* toggleButton = new QPushButton("Toggle popup");
+  toggleButton->setCheckable(true);
+
+  QVBoxLayout* vlayout = new QVBoxLayout;
+  vlayout->addWidget(focusButton);
+  vlayout->addWidget(openButton);
+  vlayout->addWidget(toggleButton);
+  topLevel->setLayout(vlayout);
+
+  ctkPopupWidget* focusPopup = new ctkPopupWidget;
+  focusPopup->setAutoHide(true);
+  QPushButton* focusPopupContent = new QPushButton("button");
+  QVBoxLayout* focusLayout = new QVBoxLayout;
+  focusLayout->addWidget(focusPopupContent);
+  focusPopup->setLayout(focusLayout);
+  focusPopup->setBaseWidget(focusButton);
+
+  QPalette palette = focusPopup->palette();
+  QLinearGradient linearGradient(QPointF(0.f, 0.f), QPointF(0.f, 0.666f));
+  linearGradient.setSpread(QGradient::PadSpread);
+  linearGradient.setCoordinateMode(QGradient::StretchToDeviceMode);
+  linearGradient.setColorAt(0, palette.color(QPalette::Window));
+  linearGradient.setColorAt(1, palette.color(QPalette::Dark));
+  palette.setBrush(QPalette::Window, linearGradient);
+  focusPopup->setPalette(palette);
+
+  ctkPopupWidget* openPopup = new ctkPopupWidget;
+  openPopup->setFrameStyle(QFrame::Box);
+  openPopup->setLineWidth(1);
+  openPopup->setAutoHide(false);
+  openPopup->setWindowOpacity(0.7);
+  QPushButton* openPopupContent = new QPushButton("Close popup");
+  QVBoxLayout* openLayout = new QVBoxLayout;
+  openLayout->addWidget(openPopupContent);
+  openPopup->setLayout(openLayout);
+  openPopup->setBaseWidget(openButton);
+  QObject::connect(openButton, SIGNAL(clicked()),
+                   openPopup, SLOT(showPopup()));
+  QObject::connect(openPopupContent, SIGNAL(clicked()),
+                   openPopup, SLOT(hidePopup()));
+                   
+  ctkPopupWidget* togglePopup = new ctkPopupWidget;
+  togglePopup->setAutoHide(false);
+  QPushButton* togglePopupContent = new QPushButton("useless button");
+  QVBoxLayout* toggleLayout = new QVBoxLayout;
+  toggleLayout->addWidget(togglePopupContent);
+  togglePopup->setLayout(toggleLayout);
+  togglePopup->setBaseWidget(toggleButton);
+  QObject::connect(toggleButton, SIGNAL(toggled(bool)),
+                   togglePopup, SLOT(showPopup(bool)));
+  togglePopup->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+  
+  popups << focusPopup << openPopup << togglePopup;
+  return topLevel;
 }
 
 //-----------------------------------------------------------------------------
@@ -54,92 +111,27 @@ int ctkPopupWidgetTest1(int argc, char * argv [] )
   popup.setLayout(layout);
 
   popup.setBaseWidget(&base);
-  //QObject::connect(&base, SIGNAL(clicked()), &popup, SLOT(showPopup()));
   base.show();
-
-  QPushButton hiddenButton("Hidden button");
-  
-  ctkPopupWidget hiddenPopup;
-  QPushButton hiddenPopupContent("Hidden popup");
-  QVBoxLayout* hiddenLayout = new QVBoxLayout;
-  hiddenLayout->addWidget(&hiddenPopupContent);
-  hiddenPopup.setLayout(hiddenLayout);
-  hiddenPopup.setBaseWidget(&hiddenButton);
-  hiddenPopup.setAutoHide(false);
   
-  QTimer::singleShot(100, &hiddenPopup, SLOT(showPopup()));
-
-  QWidget topLevel;
-  QPushButton focusButton("Focus popup");
-  QPushButton openButton("Open popup");
-  QPushButton toggleButton("Toggle popup");
-  toggleButton.setCheckable(true);
-  QSlider opacitySlider(Qt::Horizontal);
-  opacitySlider.setRange(0, 255);
-  QVBoxLayout* vlayout = new QVBoxLayout;
-  vlayout->addWidget(&focusButton);
-  vlayout->addWidget(&openButton);
-  vlayout->addWidget(&toggleButton);
-  vlayout->addWidget(&opacitySlider);
-  topLevel.setLayout(vlayout);
-
-  ctkPopupWidget focusPopup;
-  focusPopup.setFrameStyle(QFrame::Box);
-  focusPopup.setLineWidth(1);
-  focusPopup.setAutoHide(true);
-  QPushButton focusPopupContent("button");
-  QVBoxLayout* focusLayout = new QVBoxLayout;
-  focusLayout->addWidget(&focusPopupContent);
-  focusPopup.setLayout(focusLayout);
-  focusPopup.setBaseWidget(&focusButton);
+  QList<ctkPopupWidget*> popups;
+  QWidget* hiddenPanel = createPanel("Hidden", popups);
+  QWidget* scrollPanel = createPanel("Scroll", popups);
+  foreach(ctkPopupWidget* popup, popups)
+    {
+    popup->setVerticalDirection(ctkPopupWidget::TopToBottom);
+    popup->setHorizontalDirection(Qt::LeftToRight);
+    popup->setAlignment( Qt::AlignBottom | Qt::AlignLeft);
+    popup->setEasingCurve(QEasingCurve::OutElastic);
+    }
+  popups.clear();
+  QWidget* fadePanel = createPanel("Window opacity", popups);
+  foreach(ctkPopupWidget* popup, popups)
+    {
+    popup->setAnimationEffect(ctkPopupWidget::WindowOpacityFadeEffect);
+    }
 
-  ctkPopupWidget openPopup;
-  openPopup.setAutoHide(false);
-  QPushButton openPopupContent("Close popup");
-  QVBoxLayout* openLayout = new QVBoxLayout;
-  openLayout->addWidget(&openPopupContent);
-  openPopup.setLayout(openLayout);
-  openPopup.setBaseWidget(&openButton);
-  QObject::connect(&openButton, SIGNAL(clicked()),
-                   &openPopup, SLOT(showPopup()));
-  QObject::connect(&openPopupContent, SIGNAL(clicked()),
-                   &openPopup, SLOT(hidePopup()));
-                   
-  ctkPopupWidget togglePopup;
-  togglePopup.setAutoHide(false);
-  QPushButton togglePopupContent("button");
-  QVBoxLayout* toggleLayout = new QVBoxLayout;
-  toggleLayout->addWidget(&togglePopupContent);
-  togglePopup.setLayout(toggleLayout);
-  togglePopup.setBaseWidget(&toggleButton);
-  QObject::connect(&toggleButton, SIGNAL(toggled(bool)),
-                   &togglePopup, SLOT(showPopup(bool)));
-  togglePopup.setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
-  
-  ctkPopupWidget sliderPopup;
-  sliderPopup.setAutoHide(false);
-  sliderPopup.setOpacity(255);
-  QPalette palette = sliderPopup.palette();
-  QLinearGradient linearGradient(QPointF(0.f, 0.f), QPointF(0.f, 0.666f));
-  linearGradient.setSpread(QGradient::PadSpread);
-  linearGradient.setCoordinateMode(QGradient::StretchToDeviceMode);
-  linearGradient.setColorAt(0, palette.color(QPalette::Window));
-  linearGradient.setColorAt(1, palette.color(QPalette::Dark));
-  palette.setBrush(QPalette::Window, linearGradient);
-  sliderPopup.setPalette(palette);
-  QWidget sliderPopupContent;
-  QVBoxLayout* sliderLayout = new QVBoxLayout;
-  sliderLayout->addWidget(&sliderPopupContent);
-  sliderPopup.setLayout(sliderLayout);
-  sliderPopup.setBaseWidget(&opacitySlider);
-  ctkCallback callback(changeOpacity);
-  callback.setCallbackData(&sliderPopup);
-  opacitySlider.setValue(255);
-  QObject::connect(&opacitySlider, SIGNAL(valueChanged(int)),
-                   &callback, SLOT(invoke()));
-
-  topLevel.show();
-  sliderPopup.showPopup();
+  scrollPanel->show();
+  fadePanel->show();
 
   if (argc < 2 || QString(argv[1]) != "-I" )
     {

+ 418 - 42
Libs/Widgets/ctkPopupWidget.cpp

@@ -22,7 +22,9 @@
 #include <QApplication>
 #include <QDebug>
 #include <QDesktopWidget>
+#include <QDir>
 #include <QEvent>
+#include <QLabel>
 #include <QLayout>
 #include <QMouseEvent>
 #include <QPainter>
@@ -33,28 +35,11 @@
 // CTK includes
 #include "ctkPopupWidget.h"
 
-#define LEAVE_CLOSING_DELAY 0
-#define ENTER_OPENING_DELAY 0
+#define LEAVE_CLOSING_DELAY 10
+#define ENTER_OPENING_DELAY 10
 #define DEFAULT_FADING_DURATION 333 // fast enough
 
 // -------------------------------------------------------------------------
-class ctkPopupWidgetPrivate
-{
-  Q_DECLARE_PUBLIC(ctkPopupWidget);
-protected:
-  ctkPopupWidget* const q_ptr;
-public:
-  ctkPopupWidgetPrivate(ctkPopupWidget& object);
-  void init();
-
-  QWidget* BaseWidget;
-  int Alpha;
-  int WindowAlpha;
-
-  QPropertyAnimation* AlphaAnimation;
-  bool AutoHide;
-};
-
 QGradient* duplicateGradient(const QGradient* gradient)
 {
   QGradient* newGradient = 0;
@@ -93,32 +78,259 @@ QGradient* duplicateGradient(const QGradient* gradient)
 }
 
 // -------------------------------------------------------------------------
+class ctkPopupWidgetPrivate
+{
+  Q_DECLARE_PUBLIC(ctkPopupWidget);
+protected:
+  ctkPopupWidget* const q_ptr;
+public:
+  ctkPopupWidgetPrivate(ctkPopupWidget& object);
+  ~ctkPopupWidgetPrivate();
+  void init();
+  bool fitBaseWidgetSize()const;
+  Qt::Alignment pixmapAlignment()const;
+  
+  QRect closedGeometry()const;
+  QRect openGeometry()const;
+  
+  QPropertyAnimation* currentAnimation()const;
+
+  QWidget* BaseWidget;
+  bool AutoHide;
+
+  int Alpha;
+  ctkPopupWidget::AnimationEffect Effect;
+  QPropertyAnimation* AlphaAnimation;
+  int WindowAlpha;
+  QPropertyAnimation* ScrollAnimation;
+  QLabel*             PopupPixmapWidget;
+  
+  // Geometry attributes
+  Qt::Alignment   Alignment;
+  Qt::Orientation Orientation;
+  
+  ctkPopupWidget::VerticalDirection VerticalDirection;
+  Qt::LayoutDirection HorizontalDirection;
+};
+
+
+// -------------------------------------------------------------------------
 ctkPopupWidgetPrivate::ctkPopupWidgetPrivate(ctkPopupWidget& object)
   :q_ptr(&object)
 {
   this->BaseWidget = 0;
+  this->AutoHide = true;
   this->Alpha = 255;
+  this->Effect = ctkPopupWidget::ScrollEffect;
   this->WindowAlpha = 0;
   this->AlphaAnimation = 0;
-  this->AutoHide = true;
+  this->ScrollAnimation = 0;
+  this->PopupPixmapWidget = 0;
+  // Geometry attributes
+  this->Alignment = Qt::AlignJustify | Qt::AlignBottom;
+  this->Orientation = Qt::Vertical;
+  this->VerticalDirection = ctkPopupWidget::TopToBottom;
+  this->HorizontalDirection = Qt::LeftToRight;
+}
+
+// -------------------------------------------------------------------------
+ctkPopupWidgetPrivate::~ctkPopupWidgetPrivate()
+{
+  delete this->PopupPixmapWidget;
 }
 
 // -------------------------------------------------------------------------
 void ctkPopupWidgetPrivate::init()
 {
   Q_Q(ctkPopupWidget);
-  q->setAttribute(Qt::WA_NoSystemBackground);
-  q->setAttribute(Qt::WA_OpaquePaintEvent, false);
-  q->setAttribute(Qt::WA_TranslucentBackground);
+  q->setAnimationEffect(this->Effect);
   this->Alpha = q->style()->styleHint(QStyle::SH_ToolTipLabel_Opacity);
 
   this->AlphaAnimation = new QPropertyAnimation(q, "windowAlpha", q);
   this->AlphaAnimation->setDuration(DEFAULT_FADING_DURATION);
   this->AlphaAnimation->setStartValue(0);
   this->AlphaAnimation->setEndValue(this->Alpha);
-  this->AlphaAnimation->setEasingCurve(QEasingCurve::InOutQuad);
   QObject::connect(this->AlphaAnimation, SIGNAL(finished()),
                    q, SLOT(onEffectFinished()));
+
+  this->PopupPixmapWidget = new QLabel(0, Qt::ToolTip | Qt::FramelessWindowHint);
+  this->ScrollAnimation = new QPropertyAnimation(q, "windowGeometry", 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->setEasingCurve(QEasingCurve::OutCubic);
+}
+
+// -------------------------------------------------------------------------
+QPropertyAnimation* ctkPopupWidgetPrivate::currentAnimation()const
+{
+  return this->Effect == ctkPopupWidget::ScrollEffect ?
+    this->ScrollAnimation : this->AlphaAnimation;
+}
+
+// -------------------------------------------------------------------------
+Qt::Alignment ctkPopupWidgetPrivate::pixmapAlignment()const
+{
+  Q_Q(const ctkPopupWidget);
+  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);
+  /// TODO: it really doesn't handle many cases.
+  /// It's a lot of parameters to think about.
+  QRect openGeom = this->openGeometry();
+  if (this->Orientation & Qt::Vertical)
+    {
+    if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
+      {
+      openGeom.moveTop(openGeom.bottom());
+      }
+    openGeom.setHeight(0);
+    }
+  if (this->Orientation & Qt::Horizontal) 
+    {
+    if (this->HorizontalDirection == Qt::LeftToRight)
+      {
+      openGeom.moveLeft(openGeom.right());
+      }
+    openGeom.setWidth(0);
+    }
+  return openGeom;
+}
+
+// -------------------------------------------------------------------------
+QRect ctkPopupWidgetPrivate::openGeometry()const
+{
+  Q_Q(const ctkPopupWidget);
+  QSize size = q->size();
+  if (!q->testAttribute(Qt::WA_WState_Created))
+    {
+    size = q->sizeHint();
+    }
+  
+  if (!this->BaseWidget)
+    {
+    return QRect(q->pos(), size);
+    }
+
+  QRect geometry;
+  if (this->Alignment & Qt::AlignJustify)
+    {
+    if (this->Orientation & Qt::Vertical)
+      {
+      size.setWidth(this->BaseWidget->width());
+      }
+    }
+  if (this->Alignment & Qt::AlignTop &&
+      this->Alignment & Qt::AlignBottom)
+    {
+    size.setHeight(this->BaseWidget->height());
+    }
+
+  geometry.setSize(size);
+
+  QPoint topLeft = QPoint(this->BaseWidget->geometry().left(), this->BaseWidget->geometry().top());
+  QPoint bottomRight = QPoint(this->BaseWidget->geometry().right(), this->BaseWidget->geometry().bottom());
+  
+  topLeft = this->BaseWidget->parentWidget() ? this->BaseWidget->parentWidget()->mapToGlobal(topLeft) : topLeft;
+  bottomRight = this->BaseWidget->parentWidget() ? this->BaseWidget->parentWidget()->mapToGlobal(bottomRight) : 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)
+    {
+    if (this->HorizontalDirection == Qt::LeftToRight)
+      {
+      geometry.moveLeft((topLeft.x() + bottomRight.x()) / 2 - size.width() / 2);
+      }
+    else
+      {
+      geometry.moveRight((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)
+    {
+    if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
+      {
+      geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 + size.height() / 2);
+      }
+    else
+      {
+      geometry.moveBottom((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
+      }
+    }
+  return geometry;
 }
 
 // -------------------------------------------------------------------------
@@ -196,6 +408,96 @@ 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;
+  bool transparent = (d->Effect == ctkPopupWidget::WindowOpacityFadeEffect);
+  this->setAttribute(Qt::WA_NoSystemBackground, transparent);
+  //this->setAttribute(Qt::WA_OpaquePaintEvent, !transparent);
+  this->setAttribute(Qt::WA_TranslucentBackground, transparent);
+}
+
+// -------------------------------------------------------------------------
+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::Orientation ctkPopupWidget::orientation()const
+{
+  Q_D(const ctkPopupWidget);
+  return d->Orientation;
+}
+
+// -------------------------------------------------------------------------
+void ctkPopupWidget::setOrientation(Qt::Orientation orientation)
+{
+  Q_D(ctkPopupWidget);
+  d->Orientation = orientation;
+}
+
+// -------------------------------------------------------------------------
+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);
@@ -215,8 +517,13 @@ void ctkPopupWidget::paintEvent(QPaintEvent* event)
   Q_D(ctkPopupWidget);
   Q_UNUSED(event);
 
-  if (d->AlphaAnimation->state() != QAbstractAnimation::Stopped)
+  if (d->Effect == WindowOpacityFadeEffect)
     {
+    int opacity = d->Alpha;
+    if (d->AlphaAnimation->state() != QAbstractAnimation::Stopped)
+      {
+      opacity = d->WindowAlpha;
+      }
     QPainter painter(this);
     QBrush brush = this->palette().window();
     if (brush.style() == Qt::LinearGradientPattern ||
@@ -227,7 +534,7 @@ void ctkPopupWidget::paintEvent(QPaintEvent* event)
       QGradientStops stops;
       foreach(QGradientStop stop, newGradient->stops())
         {
-        stop.second.setAlpha(d->WindowAlpha);
+        stop.second.setAlpha(opacity);
         stops.push_back(stop);
         }
       newGradient->setStops(stops);
@@ -237,7 +544,7 @@ void ctkPopupWidget::paintEvent(QPaintEvent* event)
     else
       {
       QColor color = brush.color();
-      color.setAlpha(d->WindowAlpha);
+      color.setAlpha(opacity);
       brush.setColor(color);
       }
     //QColor semiTransparentColor = this->palette().window().color();
@@ -287,7 +594,9 @@ void ctkPopupWidget::updatePopup()
     {
     return;
     }
-  if (this->underMouse() || (d->BaseWidget && d->BaseWidget->underMouse()))
+  if (this->underMouse() ||
+      (d->BaseWidget && d->BaseWidget->underMouse()) ||
+      (d->PopupPixmapWidget && d->PopupPixmapWidget->underMouse()))
     {
     this->showPopup();
     }
@@ -302,41 +611,78 @@ void ctkPopupWidget::showPopup()
 {
   Q_D(ctkPopupWidget);
   if ((this->isVisible() &&
-       d->AlphaAnimation->state() == QAbstractAnimation::Stopped) ||
+       d->currentAnimation()->state() == QAbstractAnimation::Stopped) ||
       (d->BaseWidget && !d->BaseWidget->isVisible()))
     {
     return;
     }
   if (d->BaseWidget)
     {
+    /*
     QPoint bottomLeft = QPoint(d->BaseWidget->geometry().x(), d->BaseWidget->geometry().bottom());
     QPoint pos = d->BaseWidget->parentWidget() ? d->BaseWidget->parentWidget()->mapToGlobal(bottomLeft) : bottomLeft;
     this->move(pos);
     /// TODO: need some refinement
     if ((this->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag &&
-         this->width() < d->BaseWidget->width()) ||
+          this->width() < d->BaseWidget->width()) ||
         (this->sizePolicy().horizontalPolicy() & QSizePolicy::ShrinkFlag &&
-             this->width() > d->BaseWidget->width()))
+          this->width() > d->BaseWidget->width()))
       {
       // Fit to BaseWidget size
       this->resize(d->BaseWidget->width(), this->sizeHint().height());
       }
+    */
+    }
+  this->setGeometry(d->openGeometry());
+  d->currentAnimation()->setDirection(QAbstractAnimation::Forward);
+  
+  switch(d->Effect)
+    {
+    case WindowOpacityFadeEffect:
+      // just in case it wasn't visible, usually it's a no op
+      this->show();
+      break;
+    case ScrollEffect:
+      {
+      /*
+      QRect endGeometry = this->geometry();
+      if (!this->testAttribute(Qt::WA_WState_Created) &&
+          !d->fitBaseWidgetSize())
+        {
+        endGeometry.setSize(this->sizeHint());
+        }
+      QRect startGeometry = endGeometry;
+      startGeometry.setHeight(0);
+      d->PopupPixmapWidget->setGeometry(startGeometry);
+      d->ScrollAnimation->setStartValue(startGeometry);
+      d->ScrollAnimation->setEndValue(endGeometry);
+      */
+      d->PopupPixmapWidget->setGeometry(d->closedGeometry());
+      d->ScrollAnimation->setStartValue(d->closedGeometry());
+      d->ScrollAnimation->setEndValue(d->openGeometry());
+      d->PopupPixmapWidget->setAlignment(d->pixmapAlignment());
+      QPixmap pixmap = QPixmap::grabWidget(this, QRect(QPoint(0,0), d->openGeometry().size()));
+      d->PopupPixmapWidget->setPixmap(pixmap);
+      d->PopupPixmapWidget->setWindowOpacity(this->windowOpacity());
+      d->PopupPixmapWidget->show();
+      break;
+      }
+    default:
+      break;
     }
-  d->AlphaAnimation->setDirection(QAbstractAnimation::Forward);
-  switch(d->AlphaAnimation->state())
+  switch(d->currentAnimation()->state())
     {
     case QAbstractAnimation::Stopped:
-      d->AlphaAnimation->start();
+      d->WindowAlpha = 0;
+      d->currentAnimation()->start();
       break;
     case QAbstractAnimation::Paused:
-      d->AlphaAnimation->resume();
+      d->currentAnimation()->resume();
       break;
     default:
     case QAbstractAnimation::Running:
       break;
     }
-  // just in case it wasn't visible, usually it's a no op
-  this->show();
 }
 
 // --------------------------------------------------------------------------
@@ -345,18 +691,33 @@ void ctkPopupWidget::hidePopup()
   Q_D(ctkPopupWidget);
 
   if (!this->isVisible() &&
-      d->AlphaAnimation->state() == QAbstractAnimation::Stopped)
+      d->currentAnimation()->state() == QAbstractAnimation::Stopped)
     {
     return;
     }
-  d->AlphaAnimation->setDirection(QAbstractAnimation::Backward);
-  switch(d->AlphaAnimation->state())
+  d->currentAnimation()->setDirection(QAbstractAnimation::Backward);
+  switch(d->Effect)
+    {
+    case ScrollEffect:
+      {
+      d->PopupPixmapWidget->setAlignment(d->pixmapAlignment());
+      QPixmap pixmap = QPixmap::grabWidget(this, QRect(QPoint(0,0), this->size()));
+      d->PopupPixmapWidget->setPixmap(pixmap);
+      d->PopupPixmapWidget->setWindowOpacity(this->windowOpacity());
+      d->PopupPixmapWidget->show();
+      this->hide();
+      break;
+      }
+    default:
+      break;
+    }
+  switch(d->currentAnimation()->state())
     {
     case QAbstractAnimation::Stopped:
-      d->AlphaAnimation->start();
+      d->currentAnimation()->start();
       break;
     case QAbstractAnimation::Paused:
-      d->AlphaAnimation->resume();
+      d->currentAnimation()->resume();
       break;
     default:
     case QAbstractAnimation::Running:
@@ -378,3 +739,18 @@ void ctkPopupWidget::setWindowAlpha(int alpha)
   d->WindowAlpha = alpha;
   this->repaint();
 }
+
+// --------------------------------------------------------------------------
+QRect ctkPopupWidget::windowGeometry()const
+{
+  Q_D(const ctkPopupWidget);
+  return d->PopupPixmapWidget->geometry();
+}
+
+// --------------------------------------------------------------------------
+void ctkPopupWidget::setWindowGeometry(QRect newGeometry)
+{
+  Q_D(ctkPopupWidget);
+  d->PopupPixmapWidget->setGeometry(newGeometry);
+  d->PopupPixmapWidget->repaint();
+}

+ 54 - 0
Libs/Widgets/ctkPopupWidget.h

@@ -22,6 +22,7 @@
 #define __ctkPopupWidget_h
 
 // Qt includes
+#include <QEasingCurve>
 #include <QFrame>
 
 // CTK includes
@@ -40,6 +41,26 @@ class CTK_WIDGETS_EXPORT ctkPopupWidget : public QFrame
   /// is over the baseWidget and automatically closes when it leaves
   /// the widget.
   Q_PROPERTY( bool autoHide READ autoHide WRITE setAutoHide)
+  
+  /// ScrollEffect by default
+  Q_PROPERTY( AnimationEffect animationEffect READ animationEffect WRITE setAnimationEffect)
+  
+  /// QEasingCurve::InOutQuad by default
+  Q_PROPERTY( QEasingCurve::Type easingCurve READ easingCurve WRITE setEasingCurve);
+  
+  /// To vertically justify, use Qt::AlignTop | Qt::AlignBottom
+  /// Qt::AlignJustify | Qt::AlignBottom by default
+  Q_PROPERTY( Qt::Alignment alignment READ alignment WRITE setAlignment);
+  
+  /// Vertical by default
+  Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation);
+  
+  /// TopToBottom by default
+  Q_PROPERTY( ctkPopupWidget::VerticalDirection verticalDirection READ verticalDirection WRITE setVerticalDirection);
+
+  /// LeftToRight by default
+  Q_PROPERTY( Qt::LayoutDirection horizontalDirection READ horizontalDirection WRITE setHorizontalDirection);
+
 public:
   typedef QFrame Superclass;
   explicit ctkPopupWidget(QWidget* parent = 0);
@@ -56,6 +77,36 @@ public:
   
   bool autoHide()const;
   void setAutoHide(bool);
+  
+  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::Orientation orientation()const;
+  void setOrientation(Qt::Orientation 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 
@@ -73,10 +124,12 @@ protected slots:
   //void animatePopup();
   void onEffectFinished();
   void setWindowAlpha(int alpha);
+  void setWindowGeometry(QRect geometry);
 
 protected:
   QScopedPointer<ctkPopupWidgetPrivate> d_ptr;
   Q_PROPERTY(int windowAlpha READ windowAlpha WRITE setWindowAlpha DESIGNABLE false)
+  Q_PROPERTY(QRect windowGeometry READ windowGeometry WRITE setWindowGeometry DESIGNABLE false)
 
   virtual void paintEvent(QPaintEvent*);
   virtual void leaveEvent(QEvent* event);
@@ -84,6 +137,7 @@ protected:
   virtual bool eventFilter(QObject* obj, QEvent* event);
 
   int windowAlpha()const;
+  QRect windowGeometry()const;
 private:
   Q_DECLARE_PRIVATE(ctkPopupWidget);
   Q_DISABLE_COPY(ctkPopupWidget);