vsimplesearchinput.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #include "vsimplesearchinput.h"
  2. #include <QHBoxLayout>
  3. #include <QKeyEvent>
  4. #include <QFocusEvent>
  5. #include <QRegExpValidator>
  6. #include <QRegExp>
  7. #include <QLabel>
  8. #include "vlineedit.h"
  9. #include "utils/vutils.h"
  10. #include "vconfigmanager.h"
  11. extern VConfigManager *g_config;
  12. VSimpleSearchInput::VSimpleSearchInput(ISimpleSearch *p_obj, QWidget *p_parent)
  13. : QWidget(p_parent),
  14. m_obj(p_obj),
  15. m_inSearchMode(false),
  16. m_currentIdx(-1),
  17. m_wildCardEnabled(g_config->getEnableWildCardInSimpleSearch()),
  18. m_navigationKeyEnabled(false)
  19. {
  20. if (m_wildCardEnabled) {
  21. m_matchFlags = Qt::MatchWildcard | Qt::MatchWrap | Qt::MatchRecursive;
  22. } else {
  23. m_matchFlags = Qt::MatchContains | Qt::MatchWrap | Qt::MatchRecursive;
  24. }
  25. m_searchEdit = new VLineEdit();
  26. m_searchEdit->setPlaceholderText(tr("Type to search"));
  27. m_searchEdit->setCtrlKEnabled(false);
  28. connect(m_searchEdit, &QLineEdit::textChanged,
  29. this, &VSimpleSearchInput::handleEditTextChanged);
  30. QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp),
  31. m_searchEdit);
  32. m_searchEdit->setValidator(validator);
  33. m_searchEdit->installEventFilter(this);
  34. m_infoLabel = new QLabel();
  35. QLayout *layout = new QHBoxLayout();
  36. layout->addWidget(m_searchEdit);
  37. layout->addWidget(m_infoLabel);
  38. layout->setContentsMargins(0, 0, 0, 0);
  39. setLayout(layout);
  40. }
  41. void VSimpleSearchInput::clear()
  42. {
  43. m_inSearchMode = false;
  44. clearSearch();
  45. }
  46. // If it is the / leader key to trigger search mode.
  47. static bool isLeaderKey(int p_key, int p_modifiers)
  48. {
  49. return p_key == Qt::Key_Slash && p_modifiers == Qt::NoModifier;
  50. }
  51. static bool isCharKey(int p_key, int p_modifiers)
  52. {
  53. return p_key >= Qt::Key_A
  54. && p_key <= Qt::Key_Z
  55. && (p_modifiers == Qt::NoModifier || p_modifiers == Qt::ShiftModifier);
  56. }
  57. static bool isDigitKey(int p_key, int p_modifiers)
  58. {
  59. return p_key >= Qt::Key_0
  60. && p_key <= Qt::Key_9
  61. && (p_modifiers == Qt::NoModifier || p_modifiers == Qt::KeypadModifier);
  62. }
  63. static QChar keyToChar(int p_key, int p_modifiers)
  64. {
  65. if (isCharKey(p_key, p_modifiers)) {
  66. char ch = p_modifiers == Qt::ShiftModifier ? 'A' : 'a';
  67. return QChar(ch + (p_key - Qt::Key_A));
  68. } else if (isDigitKey(p_key, p_modifiers)) {
  69. return QChar('0' + (p_key - Qt::Key_0));
  70. }
  71. return QChar();
  72. }
  73. bool VSimpleSearchInput::tryHandleKeyPressEvent(QKeyEvent *p_event)
  74. {
  75. int key = p_event->key();
  76. Qt::KeyboardModifiers modifiers = p_event->modifiers();
  77. if (!m_inSearchMode) {
  78. // Try to trigger search mode.
  79. QChar ch;
  80. if (isCharKey(key, modifiers)
  81. || isDigitKey(key, modifiers)) {
  82. m_inSearchMode = true;
  83. ch = keyToChar(key, modifiers);
  84. } else if (isLeaderKey(key, modifiers)) {
  85. m_inSearchMode = true;
  86. }
  87. if (m_inSearchMode) {
  88. emit triggered(m_inSearchMode, false);
  89. clearSearch();
  90. m_searchEdit->setFocus();
  91. if (!ch.isNull()) {
  92. m_searchEdit->setText(ch);
  93. }
  94. return true;
  95. }
  96. } else {
  97. // Try to exit search mode.
  98. if (key == Qt::Key_Escape
  99. || (key == Qt::Key_BracketLeft
  100. && VUtils::isControlModifierForVim(modifiers))) {
  101. m_inSearchMode = false;
  102. emit triggered(m_inSearchMode, true);
  103. return true;
  104. }
  105. }
  106. // Ctrl+N/P to activate next hit item.
  107. if (VUtils::isControlModifierForVim(modifiers)
  108. && (key == Qt::Key_N || key == Qt::Key_P)) {
  109. int delta = key == Qt::Key_N ? 1 : -1;
  110. if (!m_inSearchMode) {
  111. m_inSearchMode = true;
  112. emit triggered(m_inSearchMode, false);
  113. m_searchEdit->setFocus();
  114. m_obj->highlightHitItems(m_hitItems);
  115. }
  116. if (!m_hitItems.isEmpty()) {
  117. m_currentIdx += delta;
  118. if (m_currentIdx < 0) {
  119. m_currentIdx = m_hitItems.size() - 1;
  120. } else if (m_currentIdx >= m_hitItems.size()) {
  121. m_currentIdx = 0;
  122. }
  123. m_obj->selectHitItem(currentItem());
  124. }
  125. return true;
  126. }
  127. // Up/Down Ctrl+K/J to navigate to next item.
  128. // QTreeWidget may not response to the key event if it does not have the focus.
  129. if (m_inSearchMode && m_navigationKeyEnabled) {
  130. if (key == Qt::Key_Down
  131. || key == Qt::Key_Up
  132. || (VUtils::isControlModifierForVim(modifiers)
  133. && (key == Qt::Key_J || key == Qt::Key_K))) {
  134. bool forward = true;
  135. if (key == Qt::Key_Up || key == Qt::Key_K) {
  136. forward = false;
  137. }
  138. m_obj->selectNextItem(forward);
  139. return true;
  140. }
  141. }
  142. return false;
  143. }
  144. void VSimpleSearchInput::clearSearch()
  145. {
  146. m_searchEdit->clear();
  147. m_hitItems.clear();
  148. m_currentIdx = -1;
  149. m_obj->clearItemsHighlight();
  150. updateInfoLabel(0, m_obj->totalNumberOfItems());
  151. }
  152. bool VSimpleSearchInput::eventFilter(QObject *p_watched, QEvent *p_event)
  153. {
  154. Q_UNUSED(p_watched);
  155. if (p_event->type() == QEvent::FocusOut) {
  156. QFocusEvent *eve = static_cast<QFocusEvent *>(p_event);
  157. if (eve->reason() != Qt::ActiveWindowFocusReason) {
  158. m_inSearchMode = false;
  159. emit triggered(m_inSearchMode, false);
  160. }
  161. }
  162. return QWidget::eventFilter(p_watched, p_event);
  163. }
  164. void VSimpleSearchInput::handleEditTextChanged(const QString &p_text)
  165. {
  166. if (!m_inSearchMode) {
  167. goto exit;
  168. }
  169. if (p_text.isEmpty()) {
  170. clearSearch();
  171. m_obj->selectHitItem(NULL);
  172. goto exit;
  173. }
  174. if (m_wildCardEnabled) {
  175. QString wildcardText(p_text.size() * 2 + 1, '*');
  176. for (int i = 0, j = 1; i < p_text.size(); ++i, j += 2) {
  177. wildcardText[j] = p_text[i];
  178. }
  179. m_hitItems = m_obj->searchItems(wildcardText, m_matchFlags);
  180. } else {
  181. m_hitItems = m_obj->searchItems(p_text, m_matchFlags);
  182. }
  183. updateInfoLabel(m_hitItems.size(), m_obj->totalNumberOfItems());
  184. m_obj->highlightHitItems(m_hitItems);
  185. m_currentIdx = m_hitItems.isEmpty() ? -1 : 0;
  186. m_obj->selectHitItem(currentItem());
  187. exit:
  188. emit inputTextChanged(p_text);
  189. }
  190. void VSimpleSearchInput::updateInfoLabel(int p_nrHit, int p_total)
  191. {
  192. m_infoLabel->setText(tr("%1/%2").arg(p_nrHit).arg(p_total));
  193. }