ctkPopupWidget.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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 <QEvent>
  19. #include <QLayout>
  20. #include <QMouseEvent>
  21. #include <QPainter>
  22. #include <QStyle>
  23. #include <QTimer>
  24. // CTK includes
  25. #include "ctkPopupWidget.h"
  26. #define LEAVE_CLOSING_DELAY 66
  27. #define ENTER_OPENING_DELAY 66
  28. #define DEFAULT_FADING_DURATION 333 // fast enough
  29. #define FADING_FPS 33 // 30 fps
  30. // -------------------------------------------------------------------------
  31. class ctkPopupWidgetPrivate
  32. {
  33. Q_DECLARE_PUBLIC(ctkPopupWidget);
  34. protected:
  35. ctkPopupWidget* const q_ptr;
  36. public:
  37. ctkPopupWidgetPrivate(ctkPopupWidget& object);
  38. void init();
  39. enum OpenStateType{
  40. Closed =0,
  41. Closing,
  42. Opening,
  43. Open
  44. };
  45. QWidget* BaseWidget;
  46. QTimer* Timer;
  47. int Alpha;
  48. int CurrentAlpha;
  49. int Duration;
  50. OpenStateType OpenState;
  51. bool AutoHide;
  52. };
  53. // -------------------------------------------------------------------------
  54. ctkPopupWidgetPrivate::ctkPopupWidgetPrivate(ctkPopupWidget& object)
  55. :q_ptr(&object)
  56. {
  57. this->BaseWidget = 0;
  58. this->Timer = 0;
  59. this->Alpha = 255;
  60. this->CurrentAlpha = 0;
  61. this->Duration = DEFAULT_FADING_DURATION;
  62. this->OpenState = Closed;
  63. this->AutoHide = true;
  64. }
  65. // -------------------------------------------------------------------------
  66. void ctkPopupWidgetPrivate::init()
  67. {
  68. Q_Q(ctkPopupWidget);
  69. q->setAttribute(Qt::WA_NoSystemBackground);
  70. q->setAttribute(Qt::WA_OpaquePaintEvent, false);
  71. q->setAttribute(Qt::WA_TranslucentBackground);
  72. //q->setAttribute(Qt::WA_PaintOnScreen);
  73. // Already by default
  74. //q->setAutoFillBackground(false);
  75. // Obsolete
  76. //q->setAttribute(Qt::WA_ContentsPropagated);
  77. this->Alpha = q->style()->styleHint(QStyle::SH_ToolTipLabel_Opacity);
  78. this->Timer = new QTimer(q);
  79. QObject::connect(this->Timer, SIGNAL(timeout()), q, SLOT(update()));
  80. }
  81. // -------------------------------------------------------------------------
  82. // Qt::FramelessWindowHint is required on Windows for Translucent background
  83. // Qt::Toolip is preferred to Qt::Popup as it would close itself at the first
  84. // click outside the widget (typically a click in the BaseWidget)
  85. ctkPopupWidget::ctkPopupWidget(QWidget* parentWidget)
  86. : Superclass(parentWidget, Qt::ToolTip | Qt::FramelessWindowHint)
  87. , d_ptr(new ctkPopupWidgetPrivate(*this))
  88. {
  89. Q_D(ctkPopupWidget);
  90. d->init();
  91. }
  92. // -------------------------------------------------------------------------
  93. ctkPopupWidget::~ctkPopupWidget()
  94. {
  95. }
  96. // -------------------------------------------------------------------------
  97. QWidget* ctkPopupWidget::baseWidget()const
  98. {
  99. Q_D(const ctkPopupWidget);
  100. return d->BaseWidget;
  101. }
  102. // -------------------------------------------------------------------------
  103. void ctkPopupWidget::setBaseWidget(QWidget* widget)
  104. {
  105. Q_D(ctkPopupWidget);
  106. if (d->BaseWidget)
  107. {
  108. d->BaseWidget->removeEventFilter(this);
  109. }
  110. d->BaseWidget = widget;
  111. if (d->BaseWidget)
  112. {
  113. d->BaseWidget->installEventFilter(this);
  114. }
  115. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  116. }
  117. // -------------------------------------------------------------------------
  118. int ctkPopupWidget::opacity()const
  119. {
  120. Q_D(const ctkPopupWidget);
  121. return d->Alpha;
  122. }
  123. // -------------------------------------------------------------------------
  124. void ctkPopupWidget::setOpacity(int alpha)
  125. {
  126. Q_D(ctkPopupWidget);
  127. d->Alpha = alpha;
  128. }
  129. // -------------------------------------------------------------------------
  130. bool ctkPopupWidget::autoHide()const
  131. {
  132. Q_D(const ctkPopupWidget);
  133. return d->AutoHide;
  134. }
  135. // -------------------------------------------------------------------------
  136. void ctkPopupWidget::setAutoHide(bool mode)
  137. {
  138. Q_D(ctkPopupWidget);
  139. d->AutoHide = mode;
  140. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  141. }
  142. // -------------------------------------------------------------------------
  143. void ctkPopupWidget::paintEvent(QPaintEvent* event)
  144. {
  145. Q_D(ctkPopupWidget);
  146. Q_UNUSED(event);
  147. QPainter painter(this);
  148. QColor semiTransparentColor = this->palette().window().color();
  149. semiTransparentColor.setAlpha(d->CurrentAlpha);
  150. painter.fillRect(this->rect(), semiTransparentColor);
  151. this->Superclass::paintEvent(event);
  152. if (d->OpenState == ctkPopupWidgetPrivate::Opening && d->CurrentAlpha < d->Alpha)
  153. {
  154. d->CurrentAlpha += d->Alpha * d->Timer->interval() / d->Duration;
  155. }
  156. else if (d->OpenState == ctkPopupWidgetPrivate::Closing && d->CurrentAlpha > 0)
  157. {
  158. d->CurrentAlpha -= d->Alpha * d->Timer->interval() / d->Duration;
  159. }
  160. if (d->CurrentAlpha >= d->Alpha)
  161. {
  162. d->CurrentAlpha = d->Alpha;
  163. d->OpenState = ctkPopupWidgetPrivate::Open;
  164. }
  165. if (d->CurrentAlpha <= 0)
  166. {
  167. d->CurrentAlpha = 0;
  168. this->hide();
  169. d->OpenState = ctkPopupWidgetPrivate::Closed;
  170. }
  171. }
  172. // --------------------------------------------------------------------------
  173. void ctkPopupWidget::leaveEvent(QEvent* event)
  174. {
  175. QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
  176. this->Superclass::leaveEvent(event);
  177. }
  178. // --------------------------------------------------------------------------
  179. void ctkPopupWidget::enterEvent(QEvent* event)
  180. {
  181. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  182. this->Superclass::enterEvent(event);
  183. }
  184. // --------------------------------------------------------------------------
  185. bool ctkPopupWidget::eventFilter(QObject* obj, QEvent* event)
  186. {
  187. Q_D(ctkPopupWidget);
  188. if (obj == d->BaseWidget &&
  189. event->type() == QEvent::Enter)
  190. {
  191. QTimer::singleShot(ENTER_OPENING_DELAY, this, SLOT(updatePopup()));
  192. }
  193. else if (obj == d->BaseWidget &&
  194. event->type() == QEvent::Leave)
  195. {
  196. QTimer::singleShot(LEAVE_CLOSING_DELAY, this, SLOT(updatePopup()));
  197. }
  198. return this->QObject::eventFilter(obj, event);
  199. }
  200. // --------------------------------------------------------------------------
  201. void ctkPopupWidget::updatePopup()
  202. {
  203. Q_D(ctkPopupWidget);
  204. if (!d->AutoHide)
  205. {
  206. return;
  207. }
  208. if (this->underMouse() || (d->BaseWidget && d->BaseWidget->underMouse()))
  209. {
  210. this->showPopup();
  211. }
  212. else
  213. {
  214. this->hidePopup();
  215. }
  216. }
  217. /*
  218. // --------------------------------------------------------------------------
  219. void ctkPopupWidget::hidePopup()
  220. {
  221. Q_D(ctkPopupWidget);
  222. if (this->underMouse() || d->BaseWidget->underMouse())
  223. {
  224. return;
  225. }
  226. d->hidePopup();
  227. }
  228. // --------------------------------------------------------------------------
  229. void ctkPopupWidget::showPopup()
  230. {
  231. Q_D(ctkPopupWidget);
  232. if ((!this->underMouse() && !d->BaseWidget->underMouse()) ||
  233. (this->isVisible() && d->CurrentAlpha == d->Alpha))
  234. {
  235. return;
  236. }
  237. d->showPopup();
  238. }
  239. */
  240. // --------------------------------------------------------------------------
  241. void ctkPopupWidget::showPopup()
  242. {
  243. Q_D(ctkPopupWidget);
  244. if (this->isVisible() && d->OpenState == ctkPopupWidgetPrivate::Open)
  245. {
  246. return;
  247. }
  248. if (d->BaseWidget)
  249. {
  250. QPoint bottomLeft = QPoint(d->BaseWidget->geometry().x(), d->BaseWidget->geometry().bottom());
  251. QPoint pos = d->BaseWidget->parentWidget() ? d->BaseWidget->parentWidget()->mapToGlobal(bottomLeft) : bottomLeft;
  252. this->move(pos);
  253. /// TODO: need some refinement
  254. if ((this->sizePolicy().horizontalPolicy() | QSizePolicy::GrowFlag &&
  255. this->width() < d->BaseWidget->width()) ||
  256. (this->sizePolicy().horizontalPolicy() | QSizePolicy::ShrinkFlag &&
  257. this->width() > d->BaseWidget->width()))
  258. {
  259. // Fit to BaseWidget size
  260. this->resize(d->BaseWidget->width(), this->sizeHint().height());
  261. }
  262. }
  263. d->OpenState = ctkPopupWidgetPrivate::Opening;
  264. d->Timer->start(FADING_FPS);
  265. this->show();
  266. }
  267. // --------------------------------------------------------------------------
  268. void ctkPopupWidget::hidePopup()
  269. {
  270. Q_D(ctkPopupWidget);
  271. if (!this->isVisible() || d->OpenState == ctkPopupWidgetPrivate::Closed)
  272. {
  273. return;
  274. }
  275. d->OpenState = ctkPopupWidgetPrivate::Closing;
  276. d->Timer->start(FADING_FPS);
  277. this->update();
  278. }