ctkBasePopupWidget.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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 <QPointer>
  26. #include <QPropertyAnimation>
  27. #include <QStyle>
  28. #include <QTimer>
  29. // CTK includes
  30. #include "ctkBasePopupWidget_p.h"
  31. // -------------------------------------------------------------------------
  32. QGradient* duplicateGradient(const QGradient* gradient)
  33. {
  34. QGradient* newGradient = 0;
  35. switch (gradient->type())
  36. {
  37. case QGradient::LinearGradient:
  38. {
  39. const QLinearGradient* linearGradient = static_cast<const QLinearGradient*>(gradient);
  40. newGradient = new QLinearGradient(linearGradient->start(), linearGradient->finalStop());
  41. break;
  42. }
  43. case QGradient::RadialGradient:
  44. {
  45. const QRadialGradient* radialGradient = static_cast<const QRadialGradient*>(gradient);
  46. newGradient = new QRadialGradient(radialGradient->center(), radialGradient->radius());
  47. break;
  48. }
  49. case QGradient::ConicalGradient:
  50. {
  51. const QConicalGradient* conicalGradient = static_cast<const QConicalGradient*>(gradient);
  52. newGradient = new QConicalGradient(conicalGradient->center(), conicalGradient->angle());
  53. break;
  54. }
  55. default:
  56. break;
  57. }
  58. if (!newGradient)
  59. {
  60. Q_ASSERT(gradient->type() != QGradient::NoGradient);
  61. return newGradient;
  62. }
  63. newGradient->setCoordinateMode(gradient->coordinateMode());
  64. newGradient->setSpread(gradient->spread());
  65. newGradient->setStops(gradient->stops());
  66. return newGradient;
  67. }
  68. // -------------------------------------------------------------------------
  69. ctkBasePopupWidgetPrivate::ctkBasePopupWidgetPrivate(ctkBasePopupWidget& object)
  70. :q_ptr(&object)
  71. {
  72. this->BaseWidget = 0;
  73. this->Effect = ctkBasePopupWidget::ScrollEffect;
  74. this->EffectDuration = 333; // in ms
  75. this->EffectAlpha = 1.;
  76. this->AlphaAnimation = 0;
  77. this->ForcedTranslucent = false;
  78. this->ScrollAnimation = 0;
  79. this->PopupPixmapWidget = 0;
  80. // Geometry attributes
  81. this->Alignment = Qt::AlignJustify | Qt::AlignBottom;
  82. this->Orientations = Qt::Vertical;
  83. this->VerticalDirection = ctkBasePopupWidget::TopToBottom;
  84. this->HorizontalDirection = Qt::LeftToRight;
  85. }
  86. // -------------------------------------------------------------------------
  87. ctkBasePopupWidgetPrivate::~ctkBasePopupWidgetPrivate()
  88. {
  89. }
  90. // -------------------------------------------------------------------------
  91. void ctkBasePopupWidgetPrivate::init()
  92. {
  93. Q_Q(ctkBasePopupWidget);
  94. // By default, Tooltips are shown only on active windows. In a popup widget
  95. // case, we sometimes aren't the active window but we still would like to
  96. // show the children tooltips.
  97. q->setAttribute(Qt::WA_AlwaysShowToolTips, true);
  98. this->AlphaAnimation = new QPropertyAnimation(q, "effectAlpha", q);
  99. this->AlphaAnimation->setDuration(this->EffectDuration);
  100. this->AlphaAnimation->setStartValue(0.);
  101. this->AlphaAnimation->setEndValue(1.);
  102. QObject::connect(this->AlphaAnimation, SIGNAL(finished()),
  103. q, SLOT(onEffectFinished()));
  104. this->PopupPixmapWidget = new QLabel(q, Qt::ToolTip | Qt::FramelessWindowHint);
  105. this->ScrollAnimation = new QPropertyAnimation(q, "effectGeometry", q);
  106. this->ScrollAnimation->setDuration(this->EffectDuration);
  107. QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
  108. q, SLOT(onEffectFinished()));
  109. QObject::connect(this->ScrollAnimation, SIGNAL(finished()),
  110. this->PopupPixmapWidget, SLOT(hide()));
  111. q->setAnimationEffect(this->Effect);
  112. q->setEasingCurve(QEasingCurve::OutCubic);
  113. q->setBaseWidget(q->parentWidget());
  114. }
  115. // -------------------------------------------------------------------------
  116. QPropertyAnimation* ctkBasePopupWidgetPrivate::currentAnimation()const
  117. {
  118. return this->Effect == ctkBasePopupWidget::ScrollEffect ?
  119. this->ScrollAnimation : this->AlphaAnimation;
  120. }
  121. // -------------------------------------------------------------------------
  122. bool ctkBasePopupWidgetPrivate::isOpening()const
  123. {
  124. return this->currentAnimation()->state() == QAbstractAnimation::Running &&
  125. this->currentAnimation()->direction() == QAbstractAnimation::Forward;
  126. }
  127. // -------------------------------------------------------------------------
  128. bool ctkBasePopupWidgetPrivate::isClosing()const
  129. {
  130. return this->currentAnimation()->state() == QAbstractAnimation::Running &&
  131. this->currentAnimation()->direction() == QAbstractAnimation::Backward;
  132. }
  133. // -------------------------------------------------------------------------
  134. bool ctkBasePopupWidgetPrivate::wasClosing()const
  135. {
  136. Q_Q(const ctkBasePopupWidget);
  137. return qobject_cast<QAbstractAnimation*>(q->sender())->direction()
  138. == QAbstractAnimation::Backward;
  139. }
  140. // -------------------------------------------------------------------------
  141. QWidgetList ctkBasePopupWidgetPrivate::focusWidgets(bool onlyVisible)const
  142. {
  143. Q_Q(const ctkBasePopupWidget);
  144. QWidgetList res;
  145. if (!onlyVisible || q->isVisible())
  146. {
  147. res << const_cast<ctkBasePopupWidget*>(q);
  148. }
  149. if (this->BaseWidget && (!onlyVisible || this->BaseWidget->isVisible()))
  150. {
  151. res << this->BaseWidget;
  152. }
  153. if (this->PopupPixmapWidget && (!onlyVisible || this->PopupPixmapWidget->isVisible()))
  154. {
  155. res << this->PopupPixmapWidget;
  156. }
  157. return res;
  158. }
  159. // -------------------------------------------------------------------------
  160. QWidget* ctkBasePopupWidgetPrivate::mouseOver()
  161. {
  162. QList<QWidget*> widgets = this->focusWidgets(true);
  163. foreach(QWidget* widget, widgets)
  164. {
  165. if (widget->underMouse())
  166. {
  167. return widget;
  168. }
  169. }
  170. // Warning QApplication::widgetAt(QCursor::pos()) can be a bit slow...
  171. const QPoint pos = QCursor::pos();
  172. QWidget* widgetUnderCursor = qApp->widgetAt(pos);
  173. foreach(const QWidget* focusWidget, widgets)
  174. {
  175. if (this->isAncestorOf(focusWidget, widgetUnderCursor) &&
  176. // Ignore when cursor is above a title bar of a focusWidget, underMouse
  177. // wouldn't have return false, but QApplication::widgetAt would return
  178. // the widget
  179. (focusWidget != widgetUnderCursor ||
  180. QRect(QPoint(0,0), focusWidget->size()).contains(
  181. focusWidget->mapFromGlobal(pos))))
  182. {
  183. return widgetUnderCursor;
  184. }
  185. }
  186. return 0;
  187. }
  188. // -------------------------------------------------------------------------
  189. bool ctkBasePopupWidgetPrivate::isAncestorOf(const QWidget* ancestor, const QWidget* child)const
  190. {
  191. while (child)
  192. {
  193. if (child == ancestor)
  194. {
  195. return true;
  196. }
  197. child = child->parentWidget();
  198. }
  199. return false;
  200. }
  201. // -------------------------------------------------------------------------
  202. void ctkBasePopupWidgetPrivate::setupPopupPixmapWidget()
  203. {
  204. Q_Q(ctkBasePopupWidget);
  205. this->PopupPixmapWidget->setAlignment(this->pixmapAlignment());
  206. QPixmap pixmap;
  207. if (q->testAttribute(Qt::WA_TranslucentBackground))
  208. {
  209. // only QImage handle transparency correctly
  210. QImage image(q->geometry().size(), QImage::Format_ARGB32);
  211. image.fill(0);
  212. q->render(&image);
  213. pixmap = QPixmap::fromImage(image);
  214. }
  215. else
  216. {
  217. pixmap = QPixmap::grabWidget(q, QRect(QPoint(0,0), q->geometry().size()));
  218. }
  219. this->PopupPixmapWidget->setPixmap(pixmap);
  220. this->PopupPixmapWidget->setAttribute(
  221. Qt::WA_TranslucentBackground, q->testAttribute(Qt::WA_TranslucentBackground));
  222. this->PopupPixmapWidget->setWindowOpacity(q->windowOpacity());
  223. }
  224. // -------------------------------------------------------------------------
  225. Qt::Alignment ctkBasePopupWidgetPrivate::pixmapAlignment()const
  226. {
  227. Qt::Alignment alignment;
  228. if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
  229. {
  230. alignment |= Qt::AlignBottom;
  231. }
  232. else// if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
  233. {
  234. alignment |= Qt::AlignTop;
  235. }
  236. if (this->HorizontalDirection == Qt::LeftToRight)
  237. {
  238. alignment |= Qt::AlignRight;
  239. }
  240. else// if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
  241. {
  242. alignment |= Qt::AlignLeft;
  243. }
  244. return alignment;
  245. }
  246. // -------------------------------------------------------------------------
  247. QRect ctkBasePopupWidgetPrivate::closedGeometry()const
  248. {
  249. Q_Q(const ctkBasePopupWidget);
  250. return this->closedGeometry(q->geometry());
  251. }
  252. // -------------------------------------------------------------------------
  253. QRect ctkBasePopupWidgetPrivate::closedGeometry(QRect openGeom)const
  254. {
  255. if (this->Orientations & Qt::Vertical)
  256. {
  257. if (this->VerticalDirection == ctkBasePopupWidget::BottomToTop)
  258. {
  259. openGeom.moveTop(openGeom.bottom());
  260. }
  261. openGeom.setHeight(0);
  262. }
  263. if (this->Orientations & Qt::Horizontal)
  264. {
  265. if (this->HorizontalDirection == Qt::RightToLeft)
  266. {
  267. openGeom.moveLeft(openGeom.right());
  268. }
  269. openGeom.setWidth(0);
  270. }
  271. return openGeom;
  272. }
  273. // -------------------------------------------------------------------------
  274. QRect ctkBasePopupWidgetPrivate::baseGeometry()const
  275. {
  276. if (!this->BaseWidget)
  277. {
  278. return QRect();
  279. }
  280. return QRect(this->mapToGlobal(this->BaseWidget->geometry().topLeft()),
  281. this->BaseWidget->size());
  282. }
  283. // -------------------------------------------------------------------------
  284. QPoint ctkBasePopupWidgetPrivate::mapToGlobal(const QPoint& baseWidgetPoint)const
  285. {
  286. QPoint mappedPoint = baseWidgetPoint;
  287. if (this->BaseWidget && this->BaseWidget->parentWidget())
  288. {
  289. mappedPoint = this->BaseWidget->parentWidget()->mapToGlobal(mappedPoint);
  290. }
  291. return mappedPoint;
  292. }
  293. // -------------------------------------------------------------------------
  294. QRect ctkBasePopupWidgetPrivate::desiredOpenGeometry()const
  295. {
  296. return this->desiredOpenGeometry(this->baseGeometry());
  297. }
  298. // -------------------------------------------------------------------------
  299. QRect ctkBasePopupWidgetPrivate::desiredOpenGeometry(QRect baseGeometry)const
  300. {
  301. Q_Q(const ctkBasePopupWidget);
  302. QSize size = q->size();
  303. if (!q->testAttribute(Qt::WA_WState_Created))
  304. {
  305. size = q->sizeHint();
  306. }
  307. if (baseGeometry.isNull())
  308. {
  309. return QRect(q->pos(), size);
  310. }
  311. QRect geometry;
  312. if (this->Alignment & Qt::AlignJustify)
  313. {
  314. if (this->Orientations & Qt::Vertical)
  315. {
  316. size.setWidth(baseGeometry.width());
  317. }
  318. }
  319. if (this->Alignment & Qt::AlignTop &&
  320. this->Alignment & Qt::AlignBottom)
  321. {
  322. size.setHeight(baseGeometry.height());
  323. }
  324. geometry.setSize(size);
  325. QPoint topLeft = baseGeometry.topLeft();
  326. QPoint bottomRight = baseGeometry.bottomRight();
  327. if (this->Alignment & Qt::AlignLeft)
  328. {
  329. if (this->HorizontalDirection == Qt::LeftToRight)
  330. {
  331. geometry.moveLeft(topLeft.x());
  332. }
  333. else
  334. {
  335. geometry.moveRight(topLeft.x() - 1);
  336. }
  337. }
  338. else if (this->Alignment & Qt::AlignRight)
  339. {
  340. if (this->HorizontalDirection == Qt::LeftToRight)
  341. {
  342. geometry.moveLeft(bottomRight.x() + 1);
  343. }
  344. else
  345. {
  346. geometry.moveRight(bottomRight.x());
  347. }
  348. }
  349. else if (this->Alignment & Qt::AlignHCenter)
  350. {
  351. geometry.moveLeft((topLeft.x() + bottomRight.x()) / 2 - size.width() / 2);
  352. }
  353. else if (this->Alignment & Qt::AlignJustify)
  354. {
  355. geometry.moveLeft(topLeft.x());
  356. }
  357. if (this->Alignment & Qt::AlignTop)
  358. {
  359. if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
  360. {
  361. geometry.moveTop(topLeft.y());
  362. }
  363. else
  364. {
  365. geometry.moveBottom(topLeft.y() - 1);
  366. }
  367. }
  368. else if (this->Alignment & Qt::AlignBottom)
  369. {
  370. if (this->VerticalDirection == ctkBasePopupWidget::TopToBottom)
  371. {
  372. geometry.moveTop(bottomRight.y() + 1);
  373. }
  374. else
  375. {
  376. geometry.moveBottom(bottomRight.y());
  377. }
  378. }
  379. else if (this->Alignment & Qt::AlignVCenter)
  380. {
  381. geometry.moveTop((topLeft.y() + bottomRight.y()) / 2 - size.height() / 2);
  382. }
  383. return geometry;
  384. }
  385. // -------------------------------------------------------------------------
  386. void ctkBasePopupWidgetPrivate::hideAll()
  387. {
  388. Q_Q(ctkBasePopupWidget);
  389. // It is possible to have the popup widget not being a popup but inside
  390. // a layout: maybe the popup has been pin-down in a way that it gets parented
  391. // In that case, there is no reason to hide the popup.
  392. if (!(q->windowFlags() & Qt::ToolTip))
  393. {
  394. return;
  395. }
  396. // Before hiding, transfer the active window flag to its parent, this will
  397. // prevent the application to send a ApplicationDeactivate signal that
  398. // doesn't need to be done.
  399. if (q->isActiveWindow() && this->BaseWidget)
  400. {
  401. qApp->setActiveWindow(this->BaseWidget->window());
  402. }
  403. q->hide();
  404. this->PopupPixmapWidget->hide();
  405. // If there is a popup open in the ctkBasePopupWidget children, then hide it
  406. // as well so we don't have a popup open while the ctkBasePopupWidget is hidden.
  407. QPointer<QWidget> activePopupWidget = qApp->activePopupWidget();
  408. if (activePopupWidget && this->isAncestorOf(q, activePopupWidget))
  409. {
  410. activePopupWidget->close();
  411. }
  412. }
  413. // -------------------------------------------------------------------------
  414. // Qt::FramelessWindowHint is required on Windows for Translucent background
  415. // Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
  416. // click outside the widget (typically a click in the BaseWidget)
  417. ctkBasePopupWidget::ctkBasePopupWidget(QWidget* parentWidget)
  418. //: Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
  419. : Superclass(parentWidget,
  420. Qt::ToolTip | Qt::FramelessWindowHint)
  421. , d_ptr(new ctkBasePopupWidgetPrivate(*this))
  422. {
  423. Q_D(ctkBasePopupWidget);
  424. d->init();
  425. }
  426. // -------------------------------------------------------------------------
  427. ctkBasePopupWidget::ctkBasePopupWidget(ctkBasePopupWidgetPrivate* pimpl, QWidget* parentWidget)
  428. //: //Superclass(QApplication::desktop()->screen(QApplication::desktop()->screenNumber(parentWidget)),
  429. : Superclass(parentWidget,
  430. Qt::ToolTip | Qt::FramelessWindowHint)
  431. , d_ptr(pimpl)
  432. {
  433. }
  434. // -------------------------------------------------------------------------
  435. ctkBasePopupWidget::~ctkBasePopupWidget()
  436. {
  437. }
  438. // -------------------------------------------------------------------------
  439. QWidget* ctkBasePopupWidget::baseWidget()const
  440. {
  441. Q_D(const ctkBasePopupWidget);
  442. return d->BaseWidget;
  443. }
  444. // -------------------------------------------------------------------------
  445. void ctkBasePopupWidget::setBaseWidget(QWidget* widget)
  446. {
  447. Q_D(ctkBasePopupWidget);
  448. if (d->BaseWidget)
  449. {
  450. //disconnect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
  451. // this, SLOT(onBaseWidgetDestroyed()));
  452. }
  453. d->BaseWidget = widget;
  454. if (d->BaseWidget)
  455. {
  456. //connect(d->BaseWidget, SIGNAL(destroyed(QObject*)),
  457. // this, SLOT(onBaseWidgetDestroyed()));
  458. }
  459. }
  460. // -------------------------------------------------------------------------
  461. void ctkBasePopupWidget::onBaseWidgetDestroyed()
  462. {
  463. Q_D(ctkBasePopupWidget);
  464. d->hideAll();
  465. this->setBaseWidget(0);
  466. // could be a property.
  467. this->deleteLater();
  468. }
  469. // -------------------------------------------------------------------------
  470. ctkBasePopupWidget::AnimationEffect ctkBasePopupWidget::animationEffect()const
  471. {
  472. Q_D(const ctkBasePopupWidget);
  473. return d->Effect;
  474. }
  475. // -------------------------------------------------------------------------
  476. void ctkBasePopupWidget::setAnimationEffect(ctkBasePopupWidget::AnimationEffect effect)
  477. {
  478. Q_D(ctkBasePopupWidget);
  479. /// TODO: handle the case where there is an animation running
  480. d->Effect = effect;
  481. }
  482. // -------------------------------------------------------------------------
  483. int ctkBasePopupWidget::effectDuration()const
  484. {
  485. Q_D(const ctkBasePopupWidget);
  486. return d->EffectDuration;
  487. }
  488. // -------------------------------------------------------------------------
  489. void ctkBasePopupWidget::setEffectDuration(int duration)
  490. {
  491. Q_D(ctkBasePopupWidget);
  492. d->EffectDuration = duration;
  493. d->AlphaAnimation->setDuration(d->EffectDuration);
  494. d->ScrollAnimation->setDuration(d->EffectDuration);
  495. }
  496. // -------------------------------------------------------------------------
  497. QEasingCurve::Type ctkBasePopupWidget::easingCurve()const
  498. {
  499. Q_D(const ctkBasePopupWidget);
  500. return d->AlphaAnimation->easingCurve().type();
  501. }
  502. // -------------------------------------------------------------------------
  503. void ctkBasePopupWidget::setEasingCurve(QEasingCurve::Type easingCurve)
  504. {
  505. Q_D(ctkBasePopupWidget);
  506. d->AlphaAnimation->setEasingCurve(easingCurve);
  507. d->ScrollAnimation->setEasingCurve(easingCurve);
  508. }
  509. // -------------------------------------------------------------------------
  510. Qt::Alignment ctkBasePopupWidget::alignment()const
  511. {
  512. Q_D(const ctkBasePopupWidget);
  513. return d->Alignment;
  514. }
  515. // -------------------------------------------------------------------------
  516. void ctkBasePopupWidget::setAlignment(Qt::Alignment alignment)
  517. {
  518. Q_D(ctkBasePopupWidget);
  519. d->Alignment = alignment;
  520. }
  521. // -------------------------------------------------------------------------
  522. Qt::Orientations ctkBasePopupWidget::orientation()const
  523. {
  524. Q_D(const ctkBasePopupWidget);
  525. return d->Orientations;
  526. }
  527. // -------------------------------------------------------------------------
  528. void ctkBasePopupWidget::setOrientation(Qt::Orientations orientations)
  529. {
  530. Q_D(ctkBasePopupWidget);
  531. d->Orientations = orientations;
  532. }
  533. // -------------------------------------------------------------------------
  534. ctkBasePopupWidget::VerticalDirection ctkBasePopupWidget::verticalDirection()const
  535. {
  536. Q_D(const ctkBasePopupWidget);
  537. return d->VerticalDirection;
  538. }
  539. // -------------------------------------------------------------------------
  540. void ctkBasePopupWidget::setVerticalDirection(ctkBasePopupWidget::VerticalDirection verticalDirection)
  541. {
  542. Q_D(ctkBasePopupWidget);
  543. d->VerticalDirection = verticalDirection;
  544. }
  545. // -------------------------------------------------------------------------
  546. Qt::LayoutDirection ctkBasePopupWidget::horizontalDirection()const
  547. {
  548. Q_D(const ctkBasePopupWidget);
  549. return d->HorizontalDirection;
  550. }
  551. // -------------------------------------------------------------------------
  552. void ctkBasePopupWidget::setHorizontalDirection(Qt::LayoutDirection horizontalDirection)
  553. {
  554. Q_D(ctkBasePopupWidget);
  555. d->HorizontalDirection = horizontalDirection;
  556. }
  557. // -------------------------------------------------------------------------
  558. void ctkBasePopupWidget::onEffectFinished()
  559. {
  560. Q_D(ctkBasePopupWidget);
  561. if (d->ForcedTranslucent)
  562. {
  563. d->ForcedTranslucent = false;
  564. this->setAttribute(Qt::WA_TranslucentBackground, false);
  565. }
  566. if (d->wasClosing())
  567. {
  568. d->hideAll();
  569. emit this->popupOpened(false);
  570. }
  571. else
  572. {
  573. this->show();
  574. emit this->popupOpened(true);
  575. }
  576. }
  577. // -------------------------------------------------------------------------
  578. bool ctkBasePopupWidget::event(QEvent* event)
  579. {
  580. switch(event->type())
  581. {
  582. case QEvent::ParentChange:
  583. // For now the base widget is the parent widget
  584. this->setBaseWidget(this->parentWidget());
  585. break;
  586. default:
  587. break;
  588. }
  589. return this->Superclass::event(event);
  590. }
  591. // -------------------------------------------------------------------------
  592. void ctkBasePopupWidget::paintEvent(QPaintEvent* event)
  593. {
  594. Q_D(ctkBasePopupWidget);
  595. Q_UNUSED(event);
  596. QPainter painter(this);
  597. QBrush brush = this->palette().window();
  598. if (brush.style() == Qt::LinearGradientPattern ||
  599. brush.style() == Qt::ConicalGradientPattern ||
  600. brush.style() == Qt::RadialGradientPattern)
  601. {
  602. QGradient* newGradient = duplicateGradient(brush.gradient());
  603. QGradientStops stops;
  604. foreach(QGradientStop stop, newGradient->stops())
  605. {
  606. stop.second.setAlpha(stop.second.alpha() * d->EffectAlpha);
  607. stops.push_back(stop);
  608. }
  609. newGradient->setStops(stops);
  610. brush = QBrush(*newGradient);
  611. delete newGradient;
  612. }
  613. else
  614. {
  615. QColor color = brush.color();
  616. color.setAlpha(color.alpha() * d->EffectAlpha);
  617. brush.setColor(color);
  618. }
  619. //QColor semiTransparentColor = this->palette().window().color();
  620. //semiTransparentColor.setAlpha(d->CurrentAlpha);
  621. painter.fillRect(this->rect(), brush);
  622. painter.end();
  623. // Let the QFrame draw itself if needed
  624. this->Superclass::paintEvent(event);
  625. }
  626. // --------------------------------------------------------------------------
  627. void ctkBasePopupWidget::showPopup()
  628. {
  629. Q_D(ctkBasePopupWidget);
  630. if ((this->isVisible() &&
  631. d->currentAnimation()->state() == QAbstractAnimation::Stopped) ||
  632. (d->BaseWidget && !d->BaseWidget->isVisible()))
  633. {
  634. return;
  635. }
  636. // If the layout has never been activated, the widget doesn't know its
  637. // minSize/maxSize and we then wouldn't know what's its true geometry.
  638. if (this->layout() && !this->testAttribute(Qt::WA_WState_Created))
  639. {
  640. this->layout()->activate();
  641. }
  642. this->setGeometry(d->desiredOpenGeometry());
  643. /// Maybe the popup doesn't allow the desiredOpenGeometry if the widget
  644. /// minimum size is larger than the desired size.
  645. QRect openGeometry = this->geometry();
  646. QRect closedGeometry = d->closedGeometry();
  647. d->currentAnimation()->setDirection(QAbstractAnimation::Forward);
  648. switch(d->Effect)
  649. {
  650. case WindowOpacityFadeEffect:
  651. if (!this->testAttribute(Qt::WA_TranslucentBackground))
  652. {
  653. d->ForcedTranslucent = true;
  654. this->setAttribute(Qt::WA_TranslucentBackground, true);
  655. }
  656. this->show();
  657. break;
  658. case ScrollEffect:
  659. {
  660. d->PopupPixmapWidget->setGeometry(closedGeometry);
  661. d->ScrollAnimation->setStartValue(closedGeometry);
  662. d->ScrollAnimation->setEndValue(openGeometry);
  663. d->setupPopupPixmapWidget();
  664. d->PopupPixmapWidget->show();
  665. break;
  666. }
  667. default:
  668. break;
  669. }
  670. switch(d->currentAnimation()->state())
  671. {
  672. case QAbstractAnimation::Stopped:
  673. d->currentAnimation()->start();
  674. break;
  675. case QAbstractAnimation::Paused:
  676. d->currentAnimation()->resume();
  677. break;
  678. default:
  679. case QAbstractAnimation::Running:
  680. break;
  681. }
  682. }
  683. // --------------------------------------------------------------------------
  684. void ctkBasePopupWidget::hidePopup()
  685. {
  686. Q_D(ctkBasePopupWidget);
  687. if (!this->isVisible() &&
  688. d->currentAnimation()->state() == QAbstractAnimation::Stopped)
  689. {
  690. return;
  691. }
  692. d->currentAnimation()->setDirection(QAbstractAnimation::Backward);
  693. QRect openGeometry = this->geometry();
  694. QRect closedGeometry = d->closedGeometry();
  695. switch(d->Effect)
  696. {
  697. case WindowOpacityFadeEffect:
  698. if (!this->testAttribute(Qt::WA_TranslucentBackground))
  699. {
  700. d->ForcedTranslucent = true;
  701. this->setAttribute(Qt::WA_TranslucentBackground, true);
  702. }
  703. break;
  704. case ScrollEffect:
  705. {
  706. d->ScrollAnimation->setStartValue(closedGeometry);
  707. d->ScrollAnimation->setEndValue(openGeometry);
  708. d->setupPopupPixmapWidget();
  709. d->PopupPixmapWidget->setGeometry(this->geometry());
  710. d->PopupPixmapWidget->show();
  711. if (this->isActiveWindow())
  712. {
  713. qApp->setActiveWindow(d->BaseWidget ? d->BaseWidget->window() : 0);
  714. }
  715. this->hide();
  716. break;
  717. }
  718. default:
  719. break;
  720. }
  721. switch(d->currentAnimation()->state())
  722. {
  723. case QAbstractAnimation::Stopped:
  724. d->currentAnimation()->start();
  725. break;
  726. case QAbstractAnimation::Paused:
  727. d->currentAnimation()->resume();
  728. break;
  729. default:
  730. case QAbstractAnimation::Running:
  731. break;
  732. }
  733. }
  734. // --------------------------------------------------------------------------
  735. double ctkBasePopupWidget::effectAlpha()const
  736. {
  737. Q_D(const ctkBasePopupWidget);
  738. return d->EffectAlpha;
  739. }
  740. // --------------------------------------------------------------------------
  741. void ctkBasePopupWidget::setEffectAlpha(double alpha)
  742. {
  743. Q_D(ctkBasePopupWidget);
  744. d->EffectAlpha = alpha;
  745. this->repaint();
  746. }
  747. // --------------------------------------------------------------------------
  748. QRect ctkBasePopupWidget::effectGeometry()const
  749. {
  750. Q_D(const ctkBasePopupWidget);
  751. return d->PopupPixmapWidget->geometry();
  752. }
  753. // --------------------------------------------------------------------------
  754. void ctkBasePopupWidget::setEffectGeometry(QRect newGeometry)
  755. {
  756. Q_D(ctkBasePopupWidget);
  757. d->PopupPixmapWidget->setGeometry(newGeometry);
  758. d->PopupPixmapWidget->repaint();
  759. }