ctkPushButton.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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.apache.org/licenses/LICENSE-2.0.txt
  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 <QLayout>
  17. #include <QPainter>
  18. #include <QStyle>
  19. #include <QStyleOptionButton>
  20. #include <QStylePainter>
  21. // CTK includes
  22. #include "ctkPushButton_p.h"
  23. //-----------------------------------------------------------------------------
  24. ctkPushButtonPrivate::ctkPushButtonPrivate(ctkPushButton& object)
  25. :q_ptr(&object)
  26. {
  27. this->ButtonTextAlignment = Qt::AlignHCenter | Qt::AlignVCenter;
  28. this->IconAlignment = Qt::AlignLeft | Qt::AlignVCenter;
  29. this->IconSpacing = 4;
  30. }
  31. //-----------------------------------------------------------------------------
  32. void ctkPushButtonPrivate::init()
  33. {
  34. }
  35. //-----------------------------------------------------------------------------
  36. QRect ctkPushButtonPrivate::iconRect()const
  37. {
  38. Q_Q(const ctkPushButton);
  39. QRect rect;
  40. QStyleOptionButton opt;
  41. q->initStyleOption(&opt);
  42. QSize iconSize = q->iconSize();
  43. int buttonHeight = opt.rect.height();
  44. uint tf = this->ButtonTextAlignment;
  45. if (q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
  46. {
  47. tf |= Qt::TextShowMnemonic;
  48. }
  49. else
  50. {
  51. tf |= Qt::TextHideMnemonic;
  52. }
  53. int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
  54. int iconSpacing = this->IconSpacing;
  55. int buttonMargin = q->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, q);
  56. if (this->IconAlignment & Qt::AlignLeft)
  57. {
  58. rect = QRect((buttonHeight - iconSize.width()) / 2,
  59. (buttonHeight - iconSize.height()) / 2,
  60. iconSize.width(), iconSize.height());
  61. }
  62. else if (this->IconAlignment & Qt::AlignHCenter)
  63. {
  64. int w = iconSize.width();
  65. if (!opt.text.isEmpty() && (this->ButtonTextAlignment & Qt::AlignHCenter))
  66. {
  67. w += textWidth + iconSpacing;
  68. }
  69. rect = QRect(opt.rect.x()+ opt.rect.width() /2 - w / 2,
  70. (buttonHeight - iconSize.height()) / 2,
  71. iconSize.width(), iconSize.height());
  72. if (this->ButtonTextAlignment & Qt::AlignLeft &&
  73. rect.left() < opt.rect.x() + buttonMargin + textWidth)
  74. {
  75. rect.moveLeft(opt.rect.x() + buttonMargin + textWidth);
  76. }
  77. else if (this->ButtonTextAlignment & Qt::AlignRight &&
  78. rect.right() > opt.rect.right() - buttonMargin - textWidth)
  79. {
  80. rect.moveRight(opt.rect.right() - buttonMargin - textWidth);
  81. }
  82. }
  83. else if (this->IconAlignment & Qt::AlignRight)
  84. {
  85. rect = QRect(opt.rect.width() - (buttonHeight - iconSize.width()) / 2
  86. - iconSize.width(),
  87. (buttonHeight - iconSize.height()) / 2,
  88. iconSize.width(), iconSize.height());
  89. }
  90. return rect;
  91. }
  92. //-----------------------------------------------------------------------------
  93. QSize ctkPushButtonPrivate::buttonSizeHint()const
  94. {
  95. Q_Q(const ctkPushButton);
  96. int w = 0, h = 0;
  97. QStyleOptionButton opt;
  98. opt.initFrom(q);
  99. // icon
  100. QSize iconSize = q->iconSize();
  101. int ih = iconSize.height();
  102. int iw = iconSize.width() + this->IconSpacing;
  103. w += iw;
  104. h = qMax(h, ih);
  105. // text
  106. QString string(q->text());
  107. bool empty = string.isEmpty();
  108. if (empty)
  109. {
  110. string = QString::fromLatin1("XXXX");
  111. }
  112. QFontMetrics fm = q->fontMetrics();
  113. QSize sz = fm.size(Qt::TextShowMnemonic, string);
  114. if(!empty || !w)
  115. {
  116. w += sz.width();
  117. }
  118. h = qMax(h, sz.height());
  119. //opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
  120. QSize buttonSize = (q->style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), q).
  121. expandedTo(QApplication::globalStrut()));
  122. return buttonSize;
  123. }
  124. //-----------------------------------------------------------------------------
  125. QStyleOptionButton ctkPushButtonPrivate::drawIcon(QPainter* p)
  126. {
  127. Q_Q(ctkPushButton);
  128. QStyleOptionButton iconOpt;
  129. iconOpt.init(q);
  130. iconOpt.rect = this->iconRect();
  131. QIcon::Mode mode = iconOpt.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled;
  132. if (mode == QIcon::Normal && iconOpt.state & QStyle::State_HasFocus)
  133. {
  134. mode = QIcon::Active;
  135. }
  136. QIcon::State state = QIcon::Off;
  137. if (iconOpt.state & QStyle::State_On)
  138. {
  139. state = QIcon::On;
  140. }
  141. QPixmap pixmap = q->icon().pixmap(iconOpt.rect.size(), mode, state);
  142. p->drawPixmap(iconOpt.rect, pixmap);
  143. return iconOpt;
  144. }
  145. //-----------------------------------------------------------------------------
  146. ctkPushButton::ctkPushButton(QWidget* _parent)
  147. : QPushButton(_parent)
  148. , d_ptr(new ctkPushButtonPrivate(*this))
  149. {
  150. Q_D(ctkPushButton);
  151. d->init();
  152. }
  153. //-----------------------------------------------------------------------------
  154. ctkPushButton::ctkPushButton(const QString& title, QWidget* _parent)
  155. : QPushButton(title, _parent)
  156. , d_ptr(new ctkPushButtonPrivate(*this))
  157. {
  158. Q_D(ctkPushButton);
  159. d->init();
  160. }
  161. //-----------------------------------------------------------------------------
  162. ctkPushButton::ctkPushButton(const QIcon& icon, const QString& title,
  163. QWidget* _parent)
  164. : QPushButton(icon, title, _parent)
  165. , d_ptr(new ctkPushButtonPrivate(*this))
  166. {
  167. Q_D(ctkPushButton);
  168. d->init();
  169. }
  170. //-----------------------------------------------------------------------------
  171. ctkPushButton::ctkPushButton(ctkPushButtonPrivate* pimpl, QWidget* _parent)
  172. : QPushButton(_parent)
  173. , d_ptr(pimpl)
  174. {
  175. }
  176. //-----------------------------------------------------------------------------
  177. ctkPushButton::~ctkPushButton()
  178. {
  179. }
  180. //-----------------------------------------------------------------------------
  181. void ctkPushButton::setButtonTextAlignment(Qt::Alignment newButtonTextAlignment)
  182. {
  183. Q_D(ctkPushButton);
  184. d->ButtonTextAlignment = newButtonTextAlignment;
  185. this->update();
  186. }
  187. //-----------------------------------------------------------------------------
  188. Qt::Alignment ctkPushButton::buttonTextAlignment()const
  189. {
  190. Q_D(const ctkPushButton);
  191. return d->ButtonTextAlignment;
  192. }
  193. //-----------------------------------------------------------------------------
  194. void ctkPushButton::setIconAlignment(Qt::Alignment newIconAlignment)
  195. {
  196. Q_D(ctkPushButton);
  197. d->IconAlignment = newIconAlignment;
  198. this->update();
  199. }
  200. //-----------------------------------------------------------------------------
  201. Qt::Alignment ctkPushButton::iconAlignment()const
  202. {
  203. Q_D(const ctkPushButton);
  204. return d->IconAlignment;
  205. }
  206. //-----------------------------------------------------------------------------
  207. QSize ctkPushButton::minimumSizeHint()const
  208. {
  209. Q_D(const ctkPushButton);
  210. return d->buttonSizeHint();
  211. }
  212. //-----------------------------------------------------------------------------
  213. QSize ctkPushButton::sizeHint()const
  214. {
  215. return this->minimumSizeHint();
  216. }
  217. //-----------------------------------------------------------------------------
  218. void ctkPushButton::paintEvent(QPaintEvent * _event)
  219. {
  220. Q_UNUSED(_event);
  221. Q_D(ctkPushButton);
  222. QPainter p(this);
  223. // Draw Button
  224. QStyleOptionButton opt;
  225. this->initStyleOption(&opt);
  226. // Checkbox size
  227. QSize iconSize = this->iconSize();
  228. // Replace the icon size by the checkbox size
  229. opt.iconSize = iconSize;
  230. // Draw the panel of the button (no text, no icon)
  231. style()->drawControl(QStyle::CE_PushButtonBevel, &opt, &p, this);
  232. // TBD is PE_PanelButtonCommand better ?
  233. //style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &opt, &p, this);
  234. //int buttonHeight = opt.rect.height();
  235. uint tf = d->ButtonTextAlignment;
  236. if (this->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
  237. {
  238. tf |= Qt::TextShowMnemonic;
  239. }
  240. else
  241. {
  242. tf |= Qt::TextHideMnemonic;
  243. }
  244. int textWidth = opt.fontMetrics.boundingRect(opt.rect, tf, opt.text).width();
  245. int buttonMargin = this->style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, this);
  246. // Draw Icon
  247. QStyleOptionButton iconOpt = d->drawIcon(&p);
  248. // Spacing between the text and the checkbox
  249. int iconSpacing = d->IconSpacing;
  250. // Draw Text
  251. if (d->ButtonTextAlignment & Qt::AlignLeft)
  252. {
  253. if (d->IconAlignment & Qt::AlignLeft)
  254. {
  255. opt.rect.setLeft(iconOpt.rect.right() + iconSpacing);
  256. }
  257. else
  258. {
  259. opt.rect.setLeft(opt.rect.x() + buttonMargin);
  260. }
  261. }
  262. else if (d->ButtonTextAlignment & Qt::AlignHCenter)
  263. {
  264. if (d->IconAlignment & Qt::AlignHCenter)
  265. {
  266. opt.rect.setLeft(iconOpt.rect.right() + iconSpacing);
  267. }
  268. else
  269. {
  270. opt.rect.setLeft(opt.rect.x() + opt.rect.width() / 2 - textWidth / 2);
  271. if (d->IconAlignment & Qt::AlignLeft)
  272. {
  273. opt.rect.setLeft( qMax(iconOpt.rect.right() + iconSpacing, opt.rect.left()) );
  274. }
  275. }
  276. }
  277. else if (d->ButtonTextAlignment & Qt::AlignRight)
  278. {
  279. if (d->IconAlignment & Qt::AlignRight)
  280. {
  281. opt.rect.setLeft(iconOpt.rect.left() - iconSpacing - textWidth);
  282. }
  283. else
  284. {
  285. opt.rect.setLeft(opt.rect.right() - buttonMargin - textWidth);
  286. }
  287. }
  288. // all the computations have been made infering the text would be left oriented
  289. tf &= ~Qt::AlignHCenter & ~Qt::AlignRight;
  290. tf |= Qt::AlignLeft;
  291. this->style()->drawItemText(&p, opt.rect, tf, opt.palette, (opt.state & QStyle::State_Enabled),
  292. opt.text, QPalette::ButtonText);
  293. }