ctkPopupWidget.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QApplication>
  16. #include <QDebug>
  17. #include <QDesktopWidget>
  18. #include <QDir>
  19. #include <QEvent>
  20. #include <QLabel>
  21. #include <QLayout>
  22. #include <QMouseEvent>
  23. #include <QMoveEvent>
  24. #include <QPainter>
  25. #include <QPropertyAnimation>
  26. #include <QStyle>
  27. #include <QTimer>
  28. // CTK includes
  29. #include "ctkPopupWidget.h"
  30. #define LEAVE_CLOSING_DELAY 100 // we don't want to be too fast to close
  31. #define ENTER_OPENING_DELAY 20 // we want to be responsive but allow "errors"
  32. #define DEFAULT_FADING_DURATION 333 // fast enough without being too slow
  33. // -------------------------------------------------------------------------
  34. QGradient* duplicateGradient(const QGradient* gradient)
  35. {
  36. QGradient* newGradient = 0;
  37. switch (gradient->type())
  38. {
  39. case QGradient::LinearGradient:
  40. {
  41. const QLinearGradient* linearGradient = static_cast<const QLinearGradient*>(gradient);
  42. newGradient = new QLinearGradient(linearGradient->start(), linearGradient->finalStop());
  43. break;
  44. }
  45. case QGradient::RadialGradient:
  46. {
  47. const QRadialGradient* radialGradient = static_cast<const QRadialGradient*>(gradient);
  48. newGradient = new QRadialGradient(radialGradient->center(), radialGradient->radius());
  49. break;
  50. }
  51. case QGradient::ConicalGradient:
  52. {
  53. const QConicalGradient* conicalGradient = static_cast<const QConicalGradient*>(gradient);
  54. newGradient = new QConicalGradient(conicalGradient->center(), conicalGradient->angle());
  55. break;
  56. }
  57. default:
  58. break;
  59. }
  60. if (!newGradient)
  61. {
  62. Q_ASSERT(gradient->type() != QGradient::NoGradient);
  63. return newGradient;
  64. }
  65. newGradient->setCoordinateMode(gradient->coordinateMode());
  66. newGradient->setSpread(gradient->spread());
  67. newGradient->setStops(gradient->stops());
  68. return newGradient;
  69. }
  70. // -------------------------------------------------------------------------
  71. class ctkPopupWidgetPrivate
  72. {
  73. Q_DECLARE_PUBLIC(ctkPopupWidget);
  74. protected:
  75. ctkPopupWidget* const q_ptr;
  76. public:
  77. ctkPopupWidgetPrivate(ctkPopupWidget& object);
  78. ~ctkPopupWidgetPrivate();
  79. void init();
  80. bool isOpening()const;
  81. bool isClosing()const;
  82. bool fitBaseWidgetSize()const;
  83. Qt::Alignment pixmapAlignment()const;
  84. void setupPopupPixmapWidget();
  85. QList<const QWidget*> focusWidgets(bool onlyVisible = false)const;
  86. // Return true if the mouse cursor is above any of the focus widgets or their
  87. // children.
  88. // If the cursor is above a child widget, install the event filter to listen
  89. // when the cursor leaves the widget.
  90. bool mouseOver();
  91. // Same as QWidget::isAncestorOf() but don't restrain to the same window
  92. // and apply it to all the focusWidgets
  93. bool isAncestorOf(const QWidget* ancestor, const QWidget* child)const;
  94. /// Return the closed geometry for the popup based on the current geometry
  95. QRect closedGeometry()const;
  96. /// Return the closed geometry for a given open geometry
  97. QRect closedGeometry(QRect openGeom)const;
  98. /// Return the desired geometry, maybe it won't happen if the size is too
  99. /// small for the popup.
  100. QRect desiredOpenGeometry()const;
  101. QPropertyAnimation* currentAnimation()const;
  102. QWidget* BaseWidget;
  103. bool AutoShow;
  104. bool AutoHide;
  105. double EffectAlpha;
  106. ctkPopupWidget::AnimationEffect Effect;
  107. QPropertyAnimation* AlphaAnimation;
  108. bool ForcedTranslucent;
  109. QPropertyAnimation* ScrollAnimation;
  110. QLabel* PopupPixmapWidget;
  111. // Geometry attributes
  112. Qt::Alignment Alignment;
  113. Qt::Orientation Orientation;
  114. ctkPopupWidget::VerticalDirection VerticalDirection;
  115. Qt::LayoutDirection HorizontalDirection;
  116. };
  117. // -------------------------------------------------------------------------
  118. ctkPopupWidgetPrivate::ctkPopupWidgetPrivate(ctkPopupWidget& object)
  119. :q_ptr(&object)
  120. {
  121. this->BaseWidget = 0;
  122. this->AutoShow = true;
  123. this->AutoHide = true;
  124. this->Effect = ctkPopupWidget::ScrollEffect;
  125. this->EffectAlpha = 1.;
  126. this->AlphaAnimation = 0;
  127. this->ForcedTranslucent = false;
  128. this->ScrollAnimation = 0;
  129. this->PopupPixmapWidget = 0;
  130. // Geometry attributes
  131. this->Alignment = Qt::AlignJustify | Qt::AlignBottom;
  132. this->Orientations = Qt::Vertical;
  133. this->VerticalDirection = ctkPopupWidget::TopToBottom;
  134. this->HorizontalDirection = Qt::LeftToRight;
  135. }
  136. // -------------------------------------------------------------------------
  137. ctkPopupWidgetPrivate::~ctkPopupWidgetPrivate()
  138. {
  139. delete this->PopupPixmapWidget;
  140. }
  141. // -------------------------------------------------------------------------
  142. void ctkPopupWidgetPrivate::init()
  143. {
  144. Q_Q(ctkPopupWidget);
  145. this->AlphaAnimation = new QPropertyAnimation(q, "effectAlpha", q);
  146. this->AlphaAnimation->setDuration(DEFAULT_FADING_DURATION);
  147. this->AlphaAnimation->setStartValue(0.);
  148. this->AlphaAnimation->setEndValue(1.);
  149. QObject::connect(this->AlphaAnimation, SIGNAL(finished()),
  150. q, SLOT(onEffectFinished()));
  151. this->PopupPixmapWidget = new QLabel(0, Qt::ToolTip | Qt::FramelessWindowHint);
  152. this->ScrollAnimation = new QPropertyAnimation(q, "effectGeometry", q);
  153. this->ScrollAnimation->setDuration(DEFAULT_FADING_DURATION);
  154. QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
  155. q, SLOT(onEffectFinished()));
  156. QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
  157. this->PopupPixmapWidget, SLOT(hide()));
  158. qApp->installEventFilter(q);
  159. q->setAnimationEffect(this->Effect);
  160. q->setEasingCurve(QEasingCurve::OutCubic);
  161. }
  162. // -------------------------------------------------------------------------
  163. QPropertyAnimation* ctkPopupWidgetPrivate::currentAnimation()const
  164. {
  165. return this->Effect == ctkPopupWidget::ScrollEffect ?
  166. this->ScrollAnimation : this->AlphaAnimation;
  167. }
  168. // -------------------------------------------------------------------------
  169. bool ctkPopupWidgetPrivate::isOpening()const
  170. {
  171. return this->currentAnimation()->state() == QAbstractAnimation::Running &&
  172. this->currentAnimation()->direction() == QAbstractAnimation::Forward;
  173. }
  174. // -------------------------------------------------------------------------
  175. bool ctkPopupWidgetPrivate::isClosing()const
  176. {
  177. return this->currentAnimation()->state() == QAbstractAnimation::Running &&
  178. this->currentAnimation()->direction() == QAbstractAnimation::Backward;
  179. }
  180. // -------------------------------------------------------------------------
  181. QList<const QWidget*> ctkPopupWidgetPrivate::focusWidgets(bool onlyVisible)const
  182. {
  183. Q_Q(const ctkPopupWidget);
  184. QList<const QWidget*> res;
  185. if (!onlyVisible || q->isVisible())
  186. {
  187. res << q;
  188. }
  189. if (this->BaseWidget && (!onlyVisible || this->BaseWidget->isVisible()))
  190. {
  191. res << this->BaseWidget;
  192. }
  193. if (this->PopupPixmapWidget && (!onlyVisible || this->PopupPixmapWidget->isVisible()))
  194. {
  195. res << this->PopupPixmapWidget;
  196. }
  197. return res;
  198. }
  199. // -------------------------------------------------------------------------
  200. bool ctkPopupWidgetPrivate::mouseOver()
  201. {
  202. Q_Q(ctkPopupWidget);
  203. QList<const QWidget*> widgets = this->focusWidgets(true);
  204. foreach(const QWidget* widget, widgets)
  205. {
  206. if (widget->underMouse())
  207. {
  208. return true;
  209. }
  210. }
  211. // Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
  212. QWidget* widgetUnderCursor = qApp->widgetAt(QCursor::pos());
  213. foreach(const QWidget* focusWidget, widgets)
  214. {
  215. if (this->isAncestorOf(focusWidget, widgetUnderCursor))
  216. {
  217. widgetUnderCursor->installEventFilter(q);
  218. return true;
  219. }
  220. }
  221. return false;
  222. }
  223. // -------------------------------------------------------------------------
  224. bool ctkPopupWidgetPrivate::isAncestorOf(const QWidget* ancestor, const QWidget* child)const
  225. {
  226. while (child)
  227. {
  228. if (child == ancestor)
  229. return true;
  230. child = child->parentWidget();
  231. }
  232. return false;
  233. }
  234. // -------------------------------------------------------------------------
  235. void ctkPopupWidgetPrivate::setupPopupPixmapWidget()
  236. {
  237. Q_Q(ctkPopupWidget);
  238. this->PopupPixmapWidget->setAlignment(this->pixmapAlignment());
  239. QPixmap pixmap;
  240. if (q->testAttribute(Qt::WA_TranslucentBackground))
  241. {
  242. // only QImage handle transparency correctly
  243. QImage image(q->geometry().size(), QImage::Format_ARGB32);
  244. image.fill(0);
  245. q->render(&image);
  246. pixmap = QPixmap::fromImage(image);
  247. }
  248. else
  249. {
  250. pixmap = QPixmap::grabWidget(q, QRect(QPoint(0,0), q->geometry().size()));
  251. }
  252. this->PopupPixmapWidget->setPixmap(pixmap);
  253. this->PopupPixmapWidget->setAttribute(
  254. Qt::WA_TranslucentBackground, q->testAttribute(Qt::WA_TranslucentBackground));
  255. this->PopupPixmapWidget->setWindowOpacity(q->windowOpacity());
  256. }
  257. // -------------------------------------------------------------------------
  258. Qt::Alignment ctkPopupWidgetPrivate::pixmapAlignment()const
  259. {
  260. Qt::Alignment alignment;
  261. if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
  262. {
  263. alignment |= Qt::AlignBottom;
  264. }
  265. else// if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
  266. {
  267. alignment |= Qt::AlignTop;
  268. }
  269. if (this->HorizontalDirection == Qt::LeftToRight)
  270. {
  271. alignment |= Qt::AlignRight;
  272. }
  273. else// if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
  274. {
  275. alignment |= Qt::AlignLeft;
  276. }
  277. return alignment;
  278. }
  279. // -------------------------------------------------------------------------
  280. QRect ctkPopupWidgetPrivate::closedGeometry()const
  281. {
  282. Q_Q(const ctkPopupWidget);
  283. return this->closedGeometry(q->geometry());
  284. }
  285. // -------------------------------------------------------------------------
  286. QRect ctkPopupWidgetPrivate::closedGeometry(QRect openGeom)const
  287. {
  288. if (this->Orientations & Qt::Vertical)
  289. {
  290. if (this->VerticalDirection == ctkPopupWidget::BottomToTop)
  291. {
  292. openGeom.moveTop(openGeom.bottom());
  293. }
  294. openGeom.setHeight(0);
  295. }
  296. if (this->Orientations & Qt::Horizontal)
  297. {
  298. if (this->HorizontalDirection == Qt::RightToLeft)
  299. {
  300. openGeom.moveLeft(openGeom.right());
  301. }
  302. openGeom.setWidth(0);
  303. }
  304. return openGeom;
  305. }
  306. // -------------------------------------------------------------------------
  307. QRect ctkPopupWidgetPrivate::desiredOpenGeometry()const
  308. {
  309. Q_Q(const ctkPopupWidget);
  310. QSize size = q->size();
  311. if (!q->testAttribute(Qt::WA_WState_Created))
  312. {
  313. size = q->sizeHint();
  314. }
  315. if (!this->BaseWidget)
  316. {
  317. return QRect(q->pos(), size);
  318. }
  319. QRect geometry;
  320. if (this->Alignment & Qt::AlignJustify)
  321. {
  322. if (this->Orientations & Qt::Vertical)
  323. {
  324. size.setWidth(this->BaseWidget->width());
  325. }
  326. }
  327. if (this->Alignment & Qt::AlignTop &&
  328. this->Alignment & Qt::AlignBottom)
  329. {
  330. size.setHeight(this->BaseWidget->height());
  331. }
  332. geometry.setSize(size);
  333. QPoint topLeft = QPoint(this->BaseWidget->geometry().left(), this->BaseWidget->geometry().top());
  334. QPoint bottomRight = QPoint(this->BaseWidget->geometry().right(), this->BaseWidget->geometry().bottom());
  335. topLeft = this->BaseWidget->parentWidget() ? this->BaseWidget->parentWidget()->mapToGlobal(topLeft) : topLeft;
  336. bottomRight = this->BaseWidget->parentWidget() ? this->BaseWidget->parentWidget()->mapToGlobal(bottomRight) : bottomRight;
  337. if (this->Alignment & Qt::AlignLeft)
  338. {
  339. if (this->HorizontalDirection == Qt::LeftToRight)
  340. {
  341. geometry.moveLeft(topLeft.x());
  342. }
  343. else
  344. {
  345. geometry.moveRight(topLeft.x());
  346. }
  347. }
  348. else if (this->Alignment & Qt::AlignRight)
  349. {
  350. if (this->HorizontalDirection == Qt::LeftToRight)
  351. {
  352. geometry.moveLeft(bottomRight.x());
  353. }
  354. else
  355. {
  356. geometry.moveRight(bottomRight.x());
  357. }
  358. }
  359. else if (this->Alignment & Qt::AlignHCenter)
  360. {
  361. geometry.moveLeft((topLeft.x() + bottomRight.x()) / 2 - size.width() / 2);
  362. }
  363. else if (this->Alignment & Qt::AlignJustify)
  364. {
  365. geometry.moveLeft(topLeft.x());
  366. }
  367. if (this->Alignment & Qt::AlignTop)
  368. {
  369. if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
  370. {
  371. geometry.moveTop(topLeft.y());
  372. }
  373. else
  374. {
  375. geometry.moveBottom(topLeft.y());
  376. }
  377. }
  378. else if (this->Alignment & Qt::AlignBottom)
  379. {
  380. if (this->VerticalDirection == ctkPopupWidget::TopToBottom)
  381. {
  382. geometry.moveTop(bottomRight.y());
  383. }
  384. else
  385. {
  386. geometry.moveBottom(bottomRight.y());
  387. }
  388. }
  389. else if (this->Alignment & Qt::AlignVCenter)
  390. {
  391. geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
  392. }
  393. return geometry;
  394. }
  395. {
  396. geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 + size.height() / 2);
  397. }
  398. else
  399. {
  400. geometry.moveBottom((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
  401. }
  402. }
  403. return geometry;
  404. }
  405. // -------------------------------------------------------------------------
  406. // Qt::FramelessWindowHint is required on Windows for Translucent background
  407. // Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
  408. // click outside the widget (typically a click in the BaseWidget)
  409. ctkPopupWidget::ctkPopupWidget(QWidget* parentWidget)
  410. : Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
  411. Qt::ToolTip | Qt::FramelessWindowHint)
  412. , d_ptr(new ctkPopupWidgetPrivate(*this))
  413. {
  414. Q_D(ctkPopupWidget);
  415. d->init();
  416. }
  417. // -------------------------------------------------------------------------
  418. ctkPopupWidget::~ctkPopupWidget()
  419. {
  420. }
  421. // -------------------------------------------------------------------------
  422. QWidget* ctkPopupWidget::baseWidget()const
  423. {
  424. Q_D(const ctkPopupWidget);
  425. return d->BaseWidget;
  426. }
  427. // -------------------------------------------------------------------------
  428. void ctkPopupWidget::setBaseWidget(QWidget* widget)
  429. {
  430. Q_D(ctkPopupWidget);
  431. if (d->BaseWidget)
  432. {
  433. d->BaseWidget->removeEventFilter(this);
  434. }
  435. d->BaseWidget = widget;
  436. if (d->BaseWidget)
  437. {
  438. d->BaseWidget->installEventFilter(this);
  439. }
  440. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  441. }
  442. // -------------------------------------------------------------------------
  443. bool ctkPopupWidget::autoShow()const
  444. {
  445. Q_D(const ctkPopupWidget);
  446. return d->AutoShow;
  447. }
  448. // -------------------------------------------------------------------------
  449. void ctkPopupWidget::setAutoShow(bool mode)
  450. {
  451. Q_D(ctkPopupWidget);
  452. d->AutoShow = mode;
  453. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  454. }
  455. // -------------------------------------------------------------------------
  456. bool ctkPopupWidget::autoHide()const
  457. {
  458. Q_D(const ctkPopupWidget);
  459. return d->AutoHide;
  460. }
  461. // -------------------------------------------------------------------------
  462. void ctkPopupWidget::setAutoHide(bool mode)
  463. {
  464. Q_D(ctkPopupWidget);
  465. d->AutoHide = mode;
  466. QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
  467. }
  468. // -------------------------------------------------------------------------
  469. ctkPopupWidget::AnimationEffect ctkPopupWidget::animationEffect()const
  470. {
  471. Q_D(const ctkPopupWidget);
  472. return d->Effect;
  473. }
  474. // -------------------------------------------------------------------------
  475. void ctkPopupWidget::setAnimationEffect(ctkPopupWidget::AnimationEffect effect)
  476. {
  477. Q_D(ctkPopupWidget);
  478. /// TODO: handle the case where there is an animation running
  479. d->Effect = effect;
  480. }
  481. // -------------------------------------------------------------------------
  482. QEasingCurve::Type ctkPopupWidget::easingCurve()const
  483. {
  484. Q_D(const ctkPopupWidget);
  485. return d->AlphaAnimation->easingCurve().type();
  486. }
  487. // -------------------------------------------------------------------------
  488. void ctkPopupWidget::setEasingCurve(QEasingCurve::Type easingCurve)
  489. {
  490. Q_D(ctkPopupWidget);
  491. d->AlphaAnimation->setEasingCurve(easingCurve);
  492. d->ScrollAnimation->setEasingCurve(easingCurve);
  493. }
  494. // -------------------------------------------------------------------------
  495. Qt::Alignment ctkPopupWidget::alignment()const
  496. {
  497. Q_D(const ctkPopupWidget);
  498. return d->Alignment;
  499. }
  500. // -------------------------------------------------------------------------
  501. void ctkPopupWidget::setAlignment(Qt::Alignment alignment)
  502. {
  503. Q_D(ctkPopupWidget);
  504. d->Alignment = alignment;
  505. }
  506. // -------------------------------------------------------------------------
  507. Qt::Orientations ctkPopupWidget::orientation()const
  508. {
  509. Q_D(const ctkPopupWidget);
  510. return d->Orientations;
  511. }
  512. // -------------------------------------------------------------------------
  513. void ctkPopupWidget::setOrientation(Qt::Orientations orientations)
  514. {
  515. Q_D(ctkPopupWidget);
  516. d->Orientations = orientations;
  517. }
  518. // -------------------------------------------------------------------------
  519. ctkPopupWidget::VerticalDirection ctkPopupWidget::verticalDirection()const
  520. {
  521. Q_D(const ctkPopupWidget);
  522. return d->VerticalDirection;
  523. }
  524. // -------------------------------------------------------------------------
  525. void ctkPopupWidget::setVerticalDirection(ctkPopupWidget::VerticalDirection verticalDirection)
  526. {
  527. Q_D(ctkPopupWidget);
  528. d->VerticalDirection = verticalDirection;
  529. }
  530. // -------------------------------------------------------------------------
  531. Qt::LayoutDirection ctkPopupWidget::horizontalDirection()const
  532. {
  533. Q_D(const ctkPopupWidget);
  534. return d->HorizontalDirection;
  535. }
  536. // -------------------------------------------------------------------------
  537. void ctkPopupWidget::setHorizontalDirection(Qt::LayoutDirection horizontalDirection)
  538. {
  539. Q_D(ctkPopupWidget);
  540. d->HorizontalDirection = horizontalDirection;
  541. }
  542. // -------------------------------------------------------------------------
  543. void ctkPopupWidget::onEffectFinished()
  544. {
  545. Q_D(ctkPopupWidget);
  546. if (d->ForcedTranslucent)
  547. {
  548. d->ForcedTranslucent = false;
  549. this->setAttribute(Qt::WA_TranslucentBackground, false);
  550. }
  551. if (qobject_cast<QAbstractAnimation*>(this->sender())->direction() == QAbstractAnimation::Backward)
  552. {
  553. this->hide();
  554. emit this->popupOpened(false);
  555. }
  556. else
  557. {
  558. this->show();
  559. emit this->popupOpened(true);
  560. }
  561. }
  562. // -------------------------------------------------------------------------
  563. void ctkPopupWidget::paintEvent(QPaintEvent* event)
  564. {
  565. Q_D(ctkPopupWidget);
  566. Q_UNUSED(event);
  567. QPainter painter(this);
  568. QBrush brush = this->palette().window();
  569. if (brush.style() == Qt::LinearGradientPattern ||
  570. brush.style() == Qt::ConicalGradientPattern ||
  571. brush.style() == Qt::RadialGradientPattern)
  572. {
  573. QGradient* newGradient = duplicateGradient(brush.gradient());
  574. QGradientStops stops;
  575. foreach(QGradientStop stop, newGradient->stops())
  576. {
  577. stop.second.setAlpha(stop.second.alpha() * d->EffectAlpha);
  578. stops.push_back(stop);
  579. }
  580. newGradient->setStops(stops);
  581. brush = QBrush(*newGradient);
  582. delete newGradient;
  583. }
  584. else
  585. {
  586. QColor color = brush.color();
  587. color.setAlpha(color.alpha() * d->EffectAlpha);
  588. brush.setColor(color);
  589. }
  590. //QColor semiTransparentColor = this->palette().window().color();
  591. //semiTransparentColor.setAlpha(d->CurrentAlpha);
  592. painter.fillRect(this->rect(), brush);
  593. // Let the QFrame draw itself if needed
  594. this->Superclass::paintEvent(event);
  595. }
  596. // --------------------------------------------------------------------------
  597. void ctkPopupWidget::leaveEvent(QEvent* event)
  598. {
  599. QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
  600. this->Superclass::leaveEvent(event);
  601. }
  602. // --------------------------------------------------------------------------
  603. void ctkPopupWidget::enterEvent(QEvent* event)
  604. {
  605. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  606. this->Superclass::enterEvent(event);
  607. }
  608. // --------------------------------------------------------------------------
  609. bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
  610. {
  611. Q_D(ctkPopupWidget);
  612. if (event->type() != QEvent::DynamicPropertyChange &&
  613. event->type() != QEvent::Paint &&
  614. event->type() != QEvent::HoverMove)
  615. {
  616. //qDebug() << event->type() << obj;
  617. }
  618. switch(event->type())
  619. {
  620. case QEvent::Move:
  621. {
  622. if (obj != d->BaseWidget)
  623. {
  624. break;
  625. }
  626. QMoveEvent* moveEvent = dynamic_cast<QMoveEvent*>(event);
  627. this->move(this->geometry().topLeft() + moveEvent->pos() - moveEvent->oldPos());
  628. this->update();
  629. break;
  630. }
  631. case QEvent::Hide:
  632. case QEvent::Close:
  633. if (obj != d->BaseWidget)
  634. {
  635. break;
  636. }
  637. case QEvent::ApplicationDeactivate:
  638. if (!d->AutoHide && (this->isVisible() || d->isOpening()))
  639. {
  640. this->setProperty("forcedClosed", d->isOpening() ? 2 : 1);
  641. }
  642. d->currentAnimation()->stop();
  643. this->hide();
  644. break;
  645. case QEvent::Show:
  646. if (obj != d->BaseWidget)
  647. {
  648. break;
  649. }
  650. case QEvent::ApplicationActivate:
  651. if (this->property("forcedClosed").toInt() > 0)
  652. {
  653. this->show();
  654. if (this->property("forcedClosed").toInt() == 2)
  655. {
  656. emit popupOpened(true);
  657. }
  658. this->setProperty("forcedClosed", 0);
  659. }
  660. else
  661. {
  662. this->updatePopup();
  663. }
  664. break;
  665. case QEvent::Resize:
  666. if (obj != d->BaseWidget ||
  667. !(d->Alignment & Qt::AlignJustify ||
  668. (d->Alignment & Qt::AlignTop && d->Alignment & Qt::AlignBottom)) ||
  669. !(d->isOpening() || this->isVisible()))
  670. {
  671. break;
  672. }
  673. // TODO: bug when the effect is WindowOpacityFadeEffect
  674. this->setGeometry(d->desiredOpenGeometry());
  675. break;
  676. case QEvent::Enter:
  677. if ( d->currentAnimation()->state() == QAbstractAnimation::Stopped )
  678. {
  679. // Maybe the user moved the mouse on the widget by mistake, don't open
  680. // the popup instantly...
  681. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  682. }
  683. else
  684. {
  685. // ... except if the popup is closing, we want to reopen it as sooon as
  686. // possible.
  687. this->updatePopup();
  688. }
  689. break;
  690. case QEvent::Leave:
  691. QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
  692. if (obj != d->BaseWidget)
  693. {
  694. obj->removeEventFilter(this);
  695. }
  696. break;
  697. }
  698. return this->QObject::eventFilter(obj, event);
  699. }
  700. // --------------------------------------------------------------------------
  701. void ctkPopupWidget::updatePopup()
  702. {
  703. Q_D(ctkPopupWidget);
  704. // Querying mouseOver can be slow, don't do it if not needed.
  705. bool mouseOver = (d->AutoShow || d->AutoHide) && d->mouseOver();
  706. if (d->AutoShow && mouseOver)
  707. {
  708. this->showPopup();
  709. }
  710. else if (d->AutoHide && !mouseOver)
  711. {
  712. this->hidePopup();
  713. }
  714. }
  715. // --------------------------------------------------------------------------
  716. void ctkPopupWidget::showPopup()
  717. {
  718. Q_D(ctkPopupWidget);
  719. if ((this->isVisible() &&
  720. d->currentAnimation()->state() == QAbstractAnimation::Stopped) ||
  721. (d->BaseWidget && !d->BaseWidget->isVisible()))
  722. {
  723. return;
  724. }
  725. // If the layout has never been activated, the widget doesn't know its
  726. // minSize/maxSize and we then wouldn't know what's its true geometry.
  727. if (this->layout() && !this->testAttribute(Qt::WA_WState_Created))
  728. {
  729. this->layout()->activate();
  730. }
  731. this->setGeometry(d->desiredOpenGeometry());
  732. /// Maybe the popup doesn't allow the desiredOpenGeometry if the widget
  733. /// minimum size is larger than the desired size.
  734. QRect openGeometry = this->geometry();
  735. QRect closedGeometry = d->closedGeometry();
  736. d->currentAnimation()->setDirection(QAbstractAnimation::Forward);
  737. switch(d->Effect)
  738. {
  739. case WindowOpacityFadeEffect:
  740. if (!this->testAttribute(Qt::WA_TranslucentBackground))
  741. {
  742. d->ForcedTranslucent = true;
  743. this->setAttribute(Qt::WA_TranslucentBackground, true);
  744. }
  745. this->show();
  746. break;
  747. case ScrollEffect:
  748. {
  749. d->PopupPixmapWidget->setGeometry(closedGeometry);
  750. d->ScrollAnimation->setStartValue(closedGeometry);
  751. d->ScrollAnimation->setEndValue(openGeometry);
  752. d->setupPopupPixmapWidget();
  753. d->PopupPixmapWidget->show();
  754. break;
  755. }
  756. default:
  757. break;
  758. }
  759. switch(d->currentAnimation()->state())
  760. {
  761. case QAbstractAnimation::Stopped:
  762. d->currentAnimation()->start();
  763. break;
  764. case QAbstractAnimation::Paused:
  765. d->currentAnimation()->resume();
  766. break;
  767. default:
  768. case QAbstractAnimation::Running:
  769. break;
  770. }
  771. }
  772. // --------------------------------------------------------------------------
  773. void ctkPopupWidget::hidePopup()
  774. {
  775. Q_D(ctkPopupWidget);
  776. if (!this->isVisible() &&
  777. d->currentAnimation()->state() == QAbstractAnimation::Stopped)
  778. {
  779. return;
  780. }
  781. d->currentAnimation()->setDirection(QAbstractAnimation::Backward);
  782. QRect openGeometry = this->geometry();
  783. QRect closedGeometry = d->closedGeometry();
  784. switch(d->Effect)
  785. {
  786. case WindowOpacityFadeEffect:
  787. if (!this->testAttribute(Qt::WA_TranslucentBackground))
  788. {
  789. d->ForcedTranslucent = true;
  790. this->setAttribute(Qt::WA_TranslucentBackground, true);
  791. }
  792. break;
  793. case ScrollEffect:
  794. {
  795. d->ScrollAnimation->setStartValue(closedGeometry);
  796. d->ScrollAnimation->setEndValue(openGeometry);
  797. d->setupPopupPixmapWidget();
  798. d->PopupPixmapWidget->show();
  799. this->hide();
  800. break;
  801. }
  802. default:
  803. break;
  804. }
  805. switch(d->currentAnimation()->state())
  806. {
  807. case QAbstractAnimation::Stopped:
  808. d->currentAnimation()->start();
  809. break;
  810. case QAbstractAnimation::Paused:
  811. d->currentAnimation()->resume();
  812. break;
  813. default:
  814. case QAbstractAnimation::Running:
  815. break;
  816. }
  817. }
  818. // --------------------------------------------------------------------------
  819. void ctkPopupWidget::pinPopup(bool pin)
  820. {
  821. this->setAutoHide(!pin);
  822. if (pin)
  823. {
  824. this->showPopup();
  825. }
  826. else
  827. {
  828. this->hidePopup();
  829. }
  830. }
  831. // --------------------------------------------------------------------------
  832. double ctkPopupWidget::effectAlpha()const
  833. {
  834. Q_D(const ctkPopupWidget);
  835. return d->EffectAlpha;
  836. }
  837. // --------------------------------------------------------------------------
  838. void ctkPopupWidget::setEffectAlpha(double alpha)
  839. {
  840. Q_D(ctkPopupWidget);
  841. d->EffectAlpha = alpha;
  842. this->repaint();
  843. }
  844. // --------------------------------------------------------------------------
  845. QRect ctkPopupWidget::effectGeometry()const
  846. {
  847. Q_D(const ctkPopupWidget);
  848. return d->PopupPixmapWidget->geometry();
  849. }
  850. // --------------------------------------------------------------------------
  851. void ctkPopupWidget::setEffectGeometry(QRect newGeometry)
  852. {
  853. Q_D(ctkPopupWidget);
  854. d->PopupPixmapWidget->setGeometry(newGeometry);
  855. d->PopupPixmapWidget->repaint();
  856. }