ctkSearchBox.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 <QDebug>
  17. #include <QIcon>
  18. #include <QMouseEvent>
  19. #include <QPainter>
  20. #include <QRect>
  21. #include <QStyleOption>
  22. // CTK includes
  23. #include "ctkSearchBox.h"
  24. // --------------------------------------------------
  25. class ctkSearchBoxPrivate
  26. {
  27. Q_DECLARE_PUBLIC(ctkSearchBox);
  28. protected:
  29. ctkSearchBox* const q_ptr;
  30. public:
  31. ctkSearchBoxPrivate(ctkSearchBox& object);
  32. void init();
  33. /// Position and size for the clear icon in the QLineEdit
  34. QRect clearRect()const;
  35. /// Position and size for the search icon in the QLineEdit
  36. QRect searchRect()const;
  37. QIcon clearIcon;
  38. QIcon searchIcon;
  39. bool showSearchIcon;
  40. bool alwaysShowClearIcon;
  41. bool hideClearIcon;
  42. #if QT_VERSION < 0x040700
  43. QString placeholderText;
  44. #endif
  45. };
  46. // --------------------------------------------------
  47. ctkSearchBoxPrivate::ctkSearchBoxPrivate(ctkSearchBox &object)
  48. : q_ptr(&object)
  49. {
  50. this->clearIcon = QIcon(":Icons/clear2.svg");
  51. this->searchIcon = QIcon(":Icons/search.svg");
  52. this->showSearchIcon = false;
  53. this->alwaysShowClearIcon = false;
  54. this->hideClearIcon = true;
  55. }
  56. // --------------------------------------------------
  57. void ctkSearchBoxPrivate::init()
  58. {
  59. Q_Q(ctkSearchBox);
  60. // Set a text by default on the QLineEdit
  61. q->setPlaceholderText(q->tr("Search..."));
  62. QObject::connect(q, SIGNAL(textChanged(QString)),
  63. q, SLOT(updateClearButtonState()));
  64. }
  65. // --------------------------------------------------
  66. QRect ctkSearchBoxPrivate::clearRect()const
  67. {
  68. Q_Q(const ctkSearchBox);
  69. QRect cRect = this->searchRect();
  70. cRect.moveLeft(q->width() - cRect.width() - cRect.left());
  71. return cRect;
  72. }
  73. // --------------------------------------------------
  74. QRect ctkSearchBoxPrivate::searchRect()const
  75. {
  76. Q_Q(const ctkSearchBox);
  77. QRect sRect = q->contentsRect();
  78. // If the QLineEdit has a frame, the icon must be shifted from
  79. // the frame line width
  80. if (q->hasFrame())
  81. {
  82. #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
  83. QStyleOptionFrameV2 opt;
  84. #else
  85. QStyleOptionFrame opt;
  86. #endif
  87. q->initStyleOption(&opt);
  88. sRect.adjust(opt.lineWidth, opt.lineWidth, -opt.lineWidth, -opt.lineWidth);
  89. }
  90. // Hardcoded: shrink by 1 pixel because some styles have a focus frame inside
  91. // the line edit frame.
  92. sRect.adjust(1, 1, -1, -1);
  93. // Square size
  94. sRect.setWidth(sRect.height());
  95. return sRect;
  96. }
  97. // --------------------------------------------------
  98. ctkSearchBox::ctkSearchBox(QWidget* _parent)
  99. : QLineEdit(_parent)
  100. , d_ptr(new ctkSearchBoxPrivate(*this))
  101. {
  102. Q_D(ctkSearchBox);
  103. d->init();
  104. }
  105. // --------------------------------------------------
  106. ctkSearchBox::~ctkSearchBox()
  107. {
  108. }
  109. #if QT_VERSION < 0x040700
  110. // --------------------------------------------------
  111. QString ctkSearchBox::placeholderText()const
  112. {
  113. Q_D(const ctkSearchBox);
  114. return d->placeholderText;
  115. }
  116. // --------------------------------------------------
  117. void ctkSearchBox::setPlaceholderText(const QString &defaultText)
  118. {
  119. Q_D(ctkSearchBox);
  120. d->placeholderText = defaultText;
  121. if (!this->hasFocus())
  122. {
  123. this->update();
  124. }
  125. }
  126. #endif
  127. // --------------------------------------------------
  128. void ctkSearchBox::setShowSearchIcon(bool show)
  129. {
  130. Q_D(ctkSearchBox);
  131. d->showSearchIcon = show;
  132. this->update();
  133. }
  134. // --------------------------------------------------
  135. bool ctkSearchBox::showSearchIcon()const
  136. {
  137. Q_D(const ctkSearchBox);
  138. return d->showSearchIcon;
  139. }
  140. // --------------------------------------------------
  141. void ctkSearchBox::setAlwaysShowClearIcon(bool show)
  142. {
  143. Q_D(ctkSearchBox);
  144. d->alwaysShowClearIcon = show;
  145. if (show == true)
  146. {
  147. d->hideClearIcon = false;
  148. }
  149. this->update();
  150. }
  151. // --------------------------------------------------
  152. bool ctkSearchBox::alwaysShowClearIcon()const
  153. {
  154. Q_D(const ctkSearchBox);
  155. return d->alwaysShowClearIcon;
  156. }
  157. // --------------------------------------------------
  158. void ctkSearchBox::setSearchIcon(const QIcon& icon)
  159. {
  160. Q_D(ctkSearchBox);
  161. d->searchIcon = icon;
  162. this->update();
  163. }
  164. // --------------------------------------------------
  165. QIcon ctkSearchBox::searchIcon()const
  166. {
  167. Q_D(const ctkSearchBox);
  168. return d->searchIcon;
  169. }
  170. // --------------------------------------------------
  171. void ctkSearchBox::setClearIcon(const QIcon& icon)
  172. {
  173. Q_D(ctkSearchBox);
  174. d->clearIcon = icon;
  175. this->update();
  176. }
  177. // --------------------------------------------------
  178. QIcon ctkSearchBox::clearIcon()const
  179. {
  180. Q_D(const ctkSearchBox);
  181. return d->clearIcon;
  182. }
  183. // --------------------------------------------------
  184. void ctkSearchBox::paintEvent(QPaintEvent * event)
  185. {
  186. Q_D(ctkSearchBox);
  187. // Draw the line edit with text.
  188. // Text has already been shifted to the right (in resizeEvent()) to leave
  189. // space for the search icon.
  190. this->Superclass::paintEvent(event);
  191. QPainter p(this);
  192. QRect cRect = d->clearRect();
  193. QRect sRect = d->showSearchIcon ? d->searchRect() : QRect();
  194. #if QT_VERSION >= 0x040700
  195. QRect r = rect();
  196. QPalette pal = palette();
  197. #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
  198. QStyleOptionFrameV2 panel;
  199. #else
  200. QStyleOptionFrame panel;
  201. #endif
  202. initStyleOption(&panel);
  203. r = this->style()->subElementRect(QStyle::SE_LineEditContents, &panel, this);
  204. r.setX(r.x() + this->textMargins().left());
  205. r.setY(r.y() + this->textMargins().top());
  206. r.setRight(r.right() - this->textMargins().right());
  207. r.setBottom(r.bottom() - this->textMargins().bottom());
  208. p.setClipRect(r);
  209. QFontMetrics fm = fontMetrics();
  210. Qt::Alignment va = QStyle::visualAlignment(this->layoutDirection(),
  211. QFlag(this->alignment()));
  212. int vscroll = 0;
  213. const int verticalMargin = 1;
  214. const int horizontalMargin = 2;
  215. switch (va & Qt::AlignVertical_Mask) {
  216. case Qt::AlignBottom:
  217. vscroll = r.y() + r.height() - fm.height() - verticalMargin;
  218. break;
  219. case Qt::AlignTop:
  220. vscroll = r.y() + verticalMargin;
  221. break;
  222. default:
  223. //center
  224. vscroll = r.y() + (r.height() - fm.height() + 1) / 2;
  225. break;
  226. }
  227. QRect lineRect(r.x() + horizontalMargin, vscroll,
  228. r.width() - 2*horizontalMargin, fm.height());
  229. int minLB = qMax(0, -fm.minLeftBearing());
  230. if (this->text().isEmpty())
  231. {
  232. if (!this->hasFocus() && !this->placeholderText().isEmpty())
  233. {
  234. QColor col = pal.text().color();
  235. col.setAlpha(128);
  236. QPen oldpen = p.pen();
  237. p.setPen(col);
  238. lineRect.adjust(minLB, 0, 0, 0);
  239. QString elidedText = fm.elidedText(this->placeholderText(), Qt::ElideRight, lineRect.width());
  240. p.drawText(lineRect, va, elidedText);
  241. p.setPen(oldpen);
  242. }
  243. }
  244. p.setClipRect(this->rect());
  245. #endif
  246. // Draw clearIcon
  247. if (!d->hideClearIcon)
  248. {
  249. QPixmap closePixmap = d->clearIcon.pixmap(cRect.size(),this->isEnabled() ? QIcon::Normal : QIcon::Disabled);
  250. this->style()->drawItemPixmap(&p, cRect, Qt::AlignCenter, closePixmap);
  251. }
  252. // Draw searchIcon
  253. if (d->showSearchIcon)
  254. {
  255. QPixmap searchPixmap = d->searchIcon.pixmap(sRect.size(), this->isEnabled() ? QIcon::Normal : QIcon::Disabled);
  256. this->style()->drawItemPixmap(&p, sRect, Qt::AlignCenter, searchPixmap);
  257. }
  258. }
  259. // --------------------------------------------------
  260. void ctkSearchBox::mousePressEvent(QMouseEvent *e)
  261. {
  262. Q_D(ctkSearchBox);
  263. if(d->clearRect().contains(e->pos()))
  264. {
  265. this->clear();
  266. emit this->textEdited(this->text());
  267. return;
  268. }
  269. if(d->showSearchIcon && d->searchRect().contains(e->pos()))
  270. {
  271. this->selectAll();
  272. return;
  273. }
  274. this->Superclass::mousePressEvent(e);
  275. }
  276. // --------------------------------------------------
  277. void ctkSearchBox::mouseMoveEvent(QMouseEvent *e)
  278. {
  279. Q_D(ctkSearchBox);
  280. if(d->clearRect().contains(e->pos()) ||
  281. (d->showSearchIcon && d->searchRect().contains(e->pos())))
  282. {
  283. this->setCursor(Qt::PointingHandCursor);
  284. }
  285. else
  286. {
  287. this->setCursor(this->isReadOnly() ? Qt::ArrowCursor : Qt::IBeamCursor);
  288. }
  289. this->Superclass::mouseMoveEvent(e);
  290. }
  291. // --------------------------------------------------
  292. void ctkSearchBox::resizeEvent(QResizeEvent * event)
  293. {
  294. Q_D(ctkSearchBox);
  295. static int iconSpacing = 0; // hardcoded,
  296. QRect cRect = d->clearRect();
  297. QRect sRect = d->showSearchIcon ? d->searchRect() : QRect();
  298. // Set 2 margins each sides of the QLineEdit, according to the icons
  299. this->setTextMargins( sRect.right() + iconSpacing, 0,
  300. event->size().width() - cRect.left() - iconSpacing,0);
  301. }
  302. // --------------------------------------------------
  303. void ctkSearchBox::updateClearButtonState()
  304. {
  305. Q_D(ctkSearchBox);
  306. if (!d->alwaysShowClearIcon)
  307. {
  308. d->hideClearIcon = this->text().isEmpty() ? true : false;
  309. }
  310. }