ctkCheckablePushButton.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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 <QCleanlooksStyle>
  17. #include <QDebug>
  18. #include <QDesktopWidget>
  19. #include <QLayout>
  20. #include <QMouseEvent>
  21. #include <QMenu>
  22. #include <QPainter>
  23. #include <QPointer>
  24. #include <QPushButton>
  25. #include <QStyle>
  26. #include <QStyleOptionButton>
  27. #include <QStylePainter>
  28. #include <QToolBar>
  29. // CTK includes
  30. #include "ctkCheckablePushButton.h"
  31. //-----------------------------------------------------------------------------
  32. class ctkCheckablePushButtonPrivate
  33. {
  34. Q_DECLARE_PUBLIC(ctkCheckablePushButton);
  35. protected:
  36. ctkCheckablePushButton* const q_ptr;
  37. public:
  38. ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object);
  39. void init();
  40. QRect checkboxRect() const;
  41. // Tuning of the button look&feel
  42. Qt::Alignment TextAlignment;
  43. Qt::Alignment IndicatorAlignment;
  44. };
  45. //-----------------------------------------------------------------------------
  46. ctkCheckablePushButtonPrivate::ctkCheckablePushButtonPrivate(ctkCheckablePushButton& object)
  47. :q_ptr(&object)
  48. {
  49. this->TextAlignment = Qt::AlignLeft | Qt::AlignVCenter;
  50. this->IndicatorAlignment = Qt::AlignLeft | Qt::AlignVCenter;
  51. }
  52. //-----------------------------------------------------------------------------
  53. void ctkCheckablePushButtonPrivate::init()
  54. {
  55. Q_Q(ctkCheckablePushButton);
  56. }
  57. //-----------------------------------------------------------------------------
  58. QRect ctkCheckablePushButtonPrivate::checkboxRect()const
  59. {
  60. Q_Q(const ctkCheckablePushButton);
  61. QRect rect;
  62. QStyleOptionButton opt;
  63. q->initStyleOption(&opt);
  64. QSize indicatorSize = QSize(q->style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, q),
  65. q->style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, q));
  66. int buttonHeight = opt.rect.height();
  67. uint tf = this->TextAlignment;
  68. if (q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
  69. {
  70. tf |= Qt::TextShowMnemonic;
  71. }
  72. else
  73. {
  74. tf |= Qt::TextHideMnemonic;
  75. }
  76. int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
  77. int indicatorSpacing = q->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, q);
  78. int buttonMargin = q->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, q);
  79. if (this->IndicatorAlignment & Qt::AlignLeft)
  80. {
  81. rect = QRect((buttonHeight - indicatorSize.width()) / 2,
  82. (buttonHeight - indicatorSize.height()) / 2,
  83. indicatorSize.width(), indicatorSize.height());
  84. }
  85. else if (this->IndicatorAlignment & Qt::AlignHCenter)
  86. {
  87. int w = indicatorSize.width();
  88. if (!opt.text.isEmpty() && (this->TextAlignment & Qt::AlignHCenter))
  89. {
  90. w += textWidth + indicatorSpacing;
  91. }
  92. rect = QRect(opt.rect.x()+ opt.rect.width() /2 - w / 2,
  93. (buttonHeight - indicatorSize.height()) / 2,
  94. indicatorSize.width(), indicatorSize.height());
  95. if (this->TextAlignment & Qt::AlignLeft &&
  96. rect.left() < opt.rect.x() + buttonMargin + textWidth)
  97. {
  98. rect.moveLeft(opt.rect.x() + buttonMargin + textWidth);
  99. }
  100. else if (this->TextAlignment & Qt::AlignRight &&
  101. rect.right() > opt.rect.right() - buttonMargin - textWidth)
  102. {
  103. rect.moveRight(opt.rect.right() - buttonMargin - textWidth);
  104. }
  105. }
  106. else if (this->IndicatorAlignment & Qt::AlignRight)
  107. {
  108. rect = QRect(opt.rect.width() - (buttonHeight - indicatorSize.width()) / 2
  109. - indicatorSize.width(),
  110. (buttonHeight - indicatorSize.height()) / 2,
  111. indicatorSize.width(), indicatorSize.height());
  112. }
  113. return rect;
  114. }
  115. //-----------------------------------------------------------------------------
  116. ctkCheckablePushButton::ctkCheckablePushButton(QWidget* _parent)
  117. :QPushButton(_parent)
  118. , d_ptr(new ctkCheckablePushButtonPrivate(*this))
  119. {
  120. Q_D(ctkCheckablePushButton);
  121. d->init();
  122. }
  123. //-----------------------------------------------------------------------------
  124. ctkCheckablePushButton::ctkCheckablePushButton(const QString& title, QWidget* _parent)
  125. :QPushButton(title, _parent)
  126. , d_ptr(new ctkCheckablePushButtonPrivate(*this))
  127. {
  128. }
  129. //-----------------------------------------------------------------------------
  130. ctkCheckablePushButton::~ctkCheckablePushButton()
  131. {
  132. }
  133. //-----------------------------------------------------------------------------
  134. void ctkCheckablePushButton::setButtonTextAlignment(Qt::Alignment textAlignment)
  135. {
  136. Q_D(ctkCheckablePushButton);
  137. d->TextAlignment = textAlignment;
  138. this->update();
  139. }
  140. //-----------------------------------------------------------------------------
  141. Qt::Alignment ctkCheckablePushButton::buttonTextAlignment()const
  142. {
  143. Q_D(const ctkCheckablePushButton);
  144. return d->TextAlignment;
  145. }
  146. //-----------------------------------------------------------------------------
  147. void ctkCheckablePushButton::setIndicatorAlignment(Qt::Alignment indicatorAlignment)
  148. {
  149. Q_D(ctkCheckablePushButton);
  150. d->IndicatorAlignment = indicatorAlignment;
  151. this->update();
  152. }
  153. //-----------------------------------------------------------------------------
  154. Qt::Alignment ctkCheckablePushButton::indicatorAlignment()const
  155. {
  156. Q_D(const ctkCheckablePushButton);
  157. return d->IndicatorAlignment;
  158. }
  159. //-----------------------------------------------------------------------------
  160. void ctkCheckablePushButton::paintEvent(QPaintEvent * _event)
  161. {
  162. Q_UNUSED(_event);
  163. Q_D(ctkCheckablePushButton);
  164. QPainter p(this);
  165. // Draw Button
  166. QStyleOptionButton opt;
  167. this->initStyleOption(&opt);
  168. QSize indicatorSize = QSize(style()->pixelMetric(QStyle::PM_IndicatorWidth, &opt, this),
  169. style()->pixelMetric(QStyle::PM_IndicatorHeight, &opt, this));
  170. opt.iconSize = indicatorSize;
  171. style()->drawControl(QStyle::CE_PushButtonBevel, &opt, &p, this);
  172. // TBD is PE_PanelButtonCommand better ?
  173. //style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
  174. int buttonHeight = opt.rect.height();
  175. uint tf = d->TextAlignment;
  176. if (this->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
  177. {
  178. tf |= Qt::TextShowMnemonic;
  179. }
  180. else
  181. {
  182. tf |= Qt::TextHideMnemonic;
  183. }
  184. int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
  185. int indicatorSpacing = this->style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &opt, this);
  186. int buttonMargin = this->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, this);
  187. // Draw Indicator
  188. QStyleOption indicatorOpt;
  189. indicatorOpt.init(this);
  190. if (this->isCheckable())
  191. {
  192. indicatorOpt.state |= QStyle::State_On;
  193. }
  194. else
  195. {
  196. indicatorOpt.state |= QStyle::State_Off;
  197. }
  198. indicatorOpt.rect = d->checkboxRect();
  199. this->style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &indicatorOpt, &p, 0);
  200. // Draw Text
  201. if (d->TextAlignment & Qt::AlignLeft)
  202. {
  203. if (d->IndicatorAlignment & Qt::AlignLeft)
  204. {
  205. opt.rect.setLeft(indicatorOpt.rect.right() + indicatorSpacing);
  206. }
  207. else
  208. {
  209. opt.rect.setLeft(opt.rect.x() + buttonMargin);
  210. }
  211. }
  212. else if (d->TextAlignment & Qt::AlignHCenter)
  213. {
  214. if (d->IndicatorAlignment & Qt::AlignHCenter)
  215. {
  216. opt.rect.setLeft(indicatorOpt.rect.right() + indicatorSpacing);
  217. }
  218. else
  219. {
  220. opt.rect.setLeft(opt.rect.x() + opt.rect.width() / 2 - textWidth / 2);
  221. if (d->IndicatorAlignment & Qt::AlignLeft)
  222. {
  223. opt.rect.setLeft( qMin(indicatorOpt.rect.left() + indicatorSpacing, opt.rect.left()) );
  224. }
  225. }
  226. }
  227. else if (d->TextAlignment & Qt::AlignRight)
  228. {
  229. if (d->IndicatorAlignment & Qt::AlignRight)
  230. {
  231. opt.rect.setLeft(indicatorOpt.rect.left() - indicatorSpacing - textWidth);
  232. }
  233. else
  234. {
  235. opt.rect.setLeft(opt.rect.right() - buttonMargin - textWidth);
  236. }
  237. }
  238. // all the computations have been made infering the text would be left oriented
  239. tf &= ~Qt::AlignHCenter & ~Qt::AlignRight;
  240. tf |= Qt::AlignLeft;
  241. this->style()->drawItemText(&p, opt.rect, tf, opt.palette, (opt.state & QStyle::State_Enabled),
  242. opt.text, QPalette::ButtonText);
  243. }
  244. //-----------------------------------------------------------------------------
  245. bool ctkCheckablePushButton::hitButton(const QPoint & _pos)const
  246. {
  247. Q_D(const ctkCheckablePushButton);
  248. return !d->checkboxRect().contains(_pos)
  249. && this->QPushButton::hitButton(_pos);
  250. }
  251. //-----------------------------------------------------------------------------
  252. void ctkCheckablePushButton::initStyleOption(QStyleOptionButton* option)const
  253. {
  254. this->QPushButton::initStyleOption(option);
  255. option->iconSize = QSize(this->style()->pixelMetric(QStyle::PM_IndicatorWidth, option, this),
  256. this->style()->pixelMetric(QStyle::PM_IndicatorHeight, option, this));
  257. }
  258. //-----------------------------------------------------------------------------
  259. void ctkCheckablePushButton::mousePressEvent(QMouseEvent *e)
  260. {
  261. Q_D(ctkCheckablePushButton);
  262. this->QPushButton::mousePressEvent(e);
  263. if (e->isAccepted())
  264. {
  265. return;
  266. }
  267. if (d->checkboxRect().contains(e->pos()))
  268. {
  269. //check the checkbox
  270. this->setCheckable(!this->isCheckable());
  271. this->update();
  272. e->accept();
  273. }
  274. }