vtreewidget.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #include "vtreewidget.h"
  2. #include <QDropEvent>
  3. #include <QKeyEvent>
  4. #include <QCoreApplication>
  5. #include <QSet>
  6. #include <QScrollBar>
  7. #include <QGraphicsOpacityEffect>
  8. #include <QTimer>
  9. #include "utils/vutils.h"
  10. #include "utils/vimnavigationforwidget.h"
  11. #include "vstyleditemdelegate.h"
  12. #define SEARCH_INPUT_NORMAL_OPACITY 0.8
  13. #define SEARCH_INPUT_IDLE_OPACITY 0.2
  14. VTreeWidget::VTreeWidget(QWidget *p_parent)
  15. : QTreeWidget(p_parent),
  16. ISimpleSearch(),
  17. m_fitContent(false)
  18. {
  19. setAttribute(Qt::WA_MacShowFocusRect, false);
  20. m_searchInput = new VSimpleSearchInput(this, this);
  21. connect(m_searchInput, &VSimpleSearchInput::triggered,
  22. this, &VTreeWidget::handleSearchModeTriggered);
  23. connect(m_searchInput, &VSimpleSearchInput::inputTextChanged,
  24. this, &VTreeWidget::handleSearchInputTextChanged);
  25. QGraphicsOpacityEffect * effect = new QGraphicsOpacityEffect(m_searchInput);
  26. effect->setOpacity(SEARCH_INPUT_NORMAL_OPACITY);
  27. m_searchInput->setGraphicsEffect(effect);
  28. m_searchInput->hide();
  29. m_searchColdTimer = new QTimer(this);
  30. m_searchColdTimer->setSingleShot(true);
  31. m_searchColdTimer->setInterval(1000);
  32. connect(m_searchColdTimer, &QTimer::timeout,
  33. this, [this]() {
  34. QGraphicsOpacityEffect *effect = getSearchInputEffect();
  35. Q_ASSERT(effect);
  36. effect->setOpacity(SEARCH_INPUT_IDLE_OPACITY);
  37. });
  38. m_delegate = new VStyledItemDelegate(this);
  39. setItemDelegate(m_delegate);
  40. connect(this, &VTreeWidget::itemExpanded,
  41. this, [this]() {
  42. if (m_fitContent) {
  43. resizeColumnToContents(0);
  44. }
  45. });
  46. }
  47. void VTreeWidget::keyPressEvent(QKeyEvent *p_event)
  48. {
  49. if (m_searchInput->tryHandleKeyPressEvent(p_event)) {
  50. return;
  51. }
  52. if (VimNavigationForWidget::injectKeyPressEventForVim(this, p_event)) {
  53. return;
  54. }
  55. QTreeWidget::keyPressEvent(p_event);
  56. }
  57. void VTreeWidget::clearAll()
  58. {
  59. m_searchInput->clear();
  60. setSearchInputVisible(false);
  61. VTreeWidget::clear();
  62. }
  63. void VTreeWidget::setSearchInputVisible(bool p_visible)
  64. {
  65. m_searchInput->setVisible(p_visible);
  66. // setViewportMargins() and setContentsMargins() do not work for QTreeWidget.
  67. // setStyleSheet(QString("padding-bottom: %1px").arg(bottomMargin));
  68. QGraphicsOpacityEffect *effect = getSearchInputEffect();
  69. Q_ASSERT(effect);
  70. effect->setOpacity(SEARCH_INPUT_NORMAL_OPACITY);
  71. }
  72. void VTreeWidget::resizeEvent(QResizeEvent *p_event)
  73. {
  74. QTreeWidget::resizeEvent(p_event);
  75. QRect contentRect = contentsRect();
  76. int width = contentRect.width();
  77. QScrollBar *vbar = verticalScrollBar();
  78. if (vbar && (vbar->minimum() != vbar->maximum())) {
  79. width -= vbar->width();
  80. }
  81. int y = height() - m_searchInput->height();
  82. QScrollBar *hbar = horizontalScrollBar();
  83. if (hbar && (hbar->minimum() != hbar->maximum())) {
  84. y -= hbar->height();
  85. }
  86. m_searchInput->setGeometry(QRect(contentRect.left(),
  87. y,
  88. width,
  89. m_searchInput->height()));
  90. }
  91. void VTreeWidget::handleSearchModeTriggered(bool p_inSearchMode, bool p_focus)
  92. {
  93. setSearchInputVisible(p_inSearchMode);
  94. if (!p_inSearchMode) {
  95. clearItemsHighlight();
  96. }
  97. if (p_focus) {
  98. setFocus();
  99. }
  100. }
  101. void VTreeWidget::dropEvent(QDropEvent *p_event)
  102. {
  103. QList<QTreeWidgetItem *> dragItems = selectedItems();
  104. int first = -1, last = -1;
  105. QTreeWidgetItem *firstItem = NULL;
  106. for (int i = 0; i < dragItems.size(); ++i) {
  107. int row = indexFromItem(dragItems[i]).row();
  108. if (row > last) {
  109. last = row;
  110. }
  111. if (first == -1 || row < first) {
  112. first = row;
  113. firstItem = dragItems[i];
  114. }
  115. }
  116. Q_ASSERT(firstItem);
  117. QTreeWidget::dropEvent(p_event);
  118. int target = indexFromItem(firstItem).row();
  119. emit rowsMoved(first, last, target);
  120. }
  121. QList<void *> VTreeWidget::searchItems(const QString &p_text,
  122. Qt::MatchFlags p_flags) const
  123. {
  124. QList<QTreeWidgetItem *> items = findItems(p_text, p_flags);
  125. QList<void *> res;
  126. res.reserve(items.size());
  127. for (int i = 0; i < items.size(); ++i) {
  128. res.append(items[i]);
  129. }
  130. return res;
  131. }
  132. void VTreeWidget::highlightHitItems(const QList<void *> &p_items)
  133. {
  134. clearItemsHighlight();
  135. QSet<QModelIndex> hitIndexes;
  136. for (auto it : p_items) {
  137. QModelIndex index = indexFromItem(static_cast<QTreeWidgetItem *>(it));
  138. if (index.isValid()) {
  139. hitIndexes.insert(index);
  140. }
  141. }
  142. if (!hitIndexes.isEmpty()) {
  143. m_delegate->setHitItems(hitIndexes);
  144. update();
  145. }
  146. }
  147. void VTreeWidget::clearItemsHighlight()
  148. {
  149. m_delegate->clearHitItems();
  150. update();
  151. }
  152. void VTreeWidget::selectHitItem(void *p_item)
  153. {
  154. setCurrentItem(static_cast<QTreeWidgetItem *>(p_item));
  155. }
  156. // Count the total number of tree @p_item.
  157. static int treeItemCount(QTreeWidgetItem *p_item)
  158. {
  159. if (!p_item) {
  160. return 0;
  161. }
  162. int child = p_item->childCount();
  163. int total = 1;
  164. for (int i = 0; i < child; ++i) {
  165. total += treeItemCount(p_item->child(i));
  166. }
  167. return total;
  168. }
  169. int VTreeWidget::totalNumberOfItems()
  170. {
  171. int total = 0;
  172. int cn = topLevelItemCount();
  173. for (int i = 0; i < cn; ++i) {
  174. total += treeItemCount(topLevelItem(i));
  175. }
  176. return total;
  177. }
  178. void VTreeWidget::handleSearchInputTextChanged(const QString &p_text)
  179. {
  180. m_searchColdTimer->stop();
  181. m_searchColdTimer->start();
  182. Q_UNUSED(p_text);
  183. QGraphicsOpacityEffect *effect = getSearchInputEffect();
  184. Q_ASSERT(effect);
  185. effect->setOpacity(0.8);
  186. }
  187. QGraphicsOpacityEffect *VTreeWidget::getSearchInputEffect() const
  188. {
  189. return static_cast<QGraphicsOpacityEffect *>(m_searchInput->graphicsEffect());
  190. }
  191. static QTreeWidgetItem *lastItemOfTree(QTreeWidgetItem *p_item)
  192. {
  193. if (p_item->isExpanded()) {
  194. Q_ASSERT(p_item->childCount() > 0);
  195. return p_item->child(p_item->childCount() - 1);
  196. } else {
  197. return p_item;
  198. }
  199. }
  200. QTreeWidgetItem *VTreeWidget::nextSibling(QTreeWidgetItem *p_item, bool p_forward)
  201. {
  202. if (!p_item) {
  203. return NULL;
  204. }
  205. QTreeWidgetItem *pa = p_item->parent();
  206. if (pa) {
  207. int idx = pa->indexOfChild(p_item);
  208. if (p_forward) {
  209. ++idx;
  210. if (idx >= pa->childCount()) {
  211. return NULL;
  212. }
  213. } else {
  214. --idx;
  215. if (idx < 0) {
  216. return NULL;
  217. }
  218. }
  219. return pa->child(idx);
  220. } else {
  221. // Top level item.
  222. int idx = indexOfTopLevelItem(p_item);
  223. if (p_forward) {
  224. ++idx;
  225. if (idx >= topLevelItemCount()) {
  226. return NULL;
  227. }
  228. } else {
  229. --idx;
  230. if (idx < 0) {
  231. return NULL;
  232. }
  233. }
  234. return topLevelItem(idx);
  235. }
  236. }
  237. void VTreeWidget::selectNextItem(bool p_forward)
  238. {
  239. if (topLevelItemCount() == 0) {
  240. return;
  241. }
  242. QTreeWidgetItem *item = currentItem();
  243. if (!item) {
  244. setCurrentItem(topLevelItem(0));
  245. return;
  246. }
  247. QTreeWidgetItem *nItem = nextItem(item, p_forward);
  248. if (nItem) {
  249. setCurrentItem(nItem);
  250. }
  251. }
  252. QTreeWidgetItem *VTreeWidget::nextItem(QTreeWidgetItem *p_item, bool p_forward)
  253. {
  254. QTreeWidgetItem *nItem = NULL;
  255. if (p_forward) {
  256. if (p_item->isExpanded()) {
  257. nItem = p_item->child(0);
  258. } else {
  259. while (!nItem && p_item) {
  260. nItem = nextSibling(p_item, true);
  261. p_item = p_item->parent();
  262. }
  263. }
  264. } else {
  265. nItem = nextSibling(p_item, false);
  266. if (!nItem) {
  267. nItem = p_item->parent();
  268. } else {
  269. nItem = lastItemOfTree(nItem);
  270. }
  271. }
  272. return nItem;
  273. }