ui: Added on-screen search feature (issue #79)
* Use menu action "Edit -> Find in View" or Ctrl + D * UI displayed as search bar at bottom of main window * Hide search bar with ECS or close button * Entering a query searches in name of graph nodes and code contents of code view * Use Enter to iterate through matches, well move match into view in graph and code view * Checkboxes allow for adding/removing results for certain views * Create Bookmark shortcut is now CTRL + S bug id = 79
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "utility/messaging/type/MessageMoveIDECursor.h"
|
||||
#include "utility/messaging/type/MessageShowErrors.h"
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
#include "qt/element/QtCodeNavigator.h"
|
||||
#include "qt/utility/QtContextMenu.h"
|
||||
@@ -163,6 +164,18 @@ void QtCodeArea::lineNumberAreaPaintEvent(QPaintEvent *event)
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCATION_ERROR:
|
||||
case LOCATION_SCREEN_SEARCH:
|
||||
if (annotation.isFocused || annotation.isActive)
|
||||
{
|
||||
focus = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
active = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCATION_TOKEN:
|
||||
case LOCATION_SCOPE:
|
||||
if (annotation.isFocused && utility::shareElement(activeSymbolIds, annotation.tokenIds))
|
||||
@@ -356,6 +369,65 @@ QRectF QtCodeArea::getLineRectForLineNumber(uint lineNumber) const
|
||||
return blockBoundingGeometry(block);
|
||||
}
|
||||
|
||||
void QtCodeArea::findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches)
|
||||
{
|
||||
const std::string& code = utility::toLowerCase(getCode());
|
||||
size_t pos = 0;
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
pos = code.find(query, pos);
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Annotation matchAnnotation;
|
||||
matchAnnotation.start = pos;
|
||||
matchAnnotation.end = pos + query.size();
|
||||
|
||||
std::pair<int, int> start = toLineColumn(matchAnnotation.start);
|
||||
matchAnnotation.startLine = start.first;
|
||||
matchAnnotation.startCol = start.second;
|
||||
|
||||
std::pair<int, int> end = toLineColumn(matchAnnotation.end);
|
||||
matchAnnotation.endLine = end.first;
|
||||
matchAnnotation.endCol = end.second;
|
||||
|
||||
// Set first 2 bits to 1 to avoid collisions
|
||||
matchAnnotation.locationId = ~(~Id(0) >> 2) + screenMatches->size() + 1;
|
||||
matchAnnotation.locationType = LOCATION_SCREEN_SEARCH;
|
||||
|
||||
matchAnnotation.isActive = false;
|
||||
matchAnnotation.isFocused = false;
|
||||
|
||||
m_annotations.push_back(matchAnnotation);
|
||||
screenMatches->push_back(std::make_pair(this, matchAnnotation.locationId));
|
||||
|
||||
pos += query.size();
|
||||
}
|
||||
|
||||
if (screenMatches->size() && screenMatches->back().first == this)
|
||||
{
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
|
||||
void QtCodeArea::clearScreenMatches()
|
||||
{
|
||||
size_t i = m_annotations.size();
|
||||
while (i > 0 && m_annotations[i - 1].locationType == LOCATION_SCREEN_SEARCH)
|
||||
{
|
||||
i--;
|
||||
m_linesToRehighlight.push_back(m_annotations[i].startLine - getStartLineNumber());
|
||||
}
|
||||
|
||||
if (i != m_annotations.size())
|
||||
{
|
||||
m_annotations.erase(m_annotations.begin() + i, m_annotations.end());
|
||||
viewport()->update();
|
||||
}
|
||||
}
|
||||
|
||||
void QtCodeArea::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
QPlainTextEdit::resizeEvent(e);
|
||||
|
||||
@@ -83,6 +83,9 @@ public:
|
||||
|
||||
QRectF getLineRectForLineNumber(uint lineNumber) const;
|
||||
|
||||
void findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches);
|
||||
void clearScreenMatches();
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE;
|
||||
virtual void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
|
||||
|
||||
@@ -549,7 +549,7 @@ const QtCodeField::AnnotationColor& QtCodeField::getAnnotationColorForAnnotation
|
||||
if (!s_annotationColors.size())
|
||||
{
|
||||
ColorScheme* scheme = ColorScheme::getInstance().get();
|
||||
std::vector<std::string> types = { "token", "local_symbol", "scope", "error", "fulltext" };
|
||||
std::vector<std::string> types = { "token", "local_symbol", "scope", "error", "fulltext_search", "screen_search" };
|
||||
std::vector<ColorScheme::ColorState> states = { ColorScheme::NORMAL, ColorScheme::FOCUS, ColorScheme::ACTIVE };
|
||||
|
||||
for (const std::string& type : types)
|
||||
@@ -579,10 +579,14 @@ const QtCodeField::AnnotationColor& QtCodeField::getAnnotationColorForAnnotation
|
||||
{
|
||||
i = 9;
|
||||
}
|
||||
else if (annotation.locationType == LOCATION_FULLTEXT)
|
||||
else if (annotation.locationType == LOCATION_FULLTEXT_SEARCH)
|
||||
{
|
||||
i = 12;
|
||||
}
|
||||
else if (annotation.locationType == LOCATION_SCREEN_SEARCH)
|
||||
{
|
||||
i = 15;
|
||||
}
|
||||
|
||||
if (annotation.isActive)
|
||||
{
|
||||
|
||||
@@ -99,6 +99,7 @@ protected:
|
||||
|
||||
std::vector<Annotation> m_annotations;
|
||||
std::vector<const Annotation*> m_hoveredAnnotations;
|
||||
std::vector<int> m_linesToRehighlight;
|
||||
|
||||
private:
|
||||
static std::vector<AnnotationColor> s_annotationColors;
|
||||
@@ -113,7 +114,6 @@ private:
|
||||
QtHighlighter* m_highlighter;
|
||||
|
||||
std::vector<int> m_lineLengths;
|
||||
std::vector<int> m_linesToRehighlight;
|
||||
|
||||
int m_endTextEditPosition;
|
||||
};
|
||||
|
||||
@@ -143,13 +143,12 @@ QtCodeSnippet* QtCodeFile::addCodeSnippet(const CodeSnippetParams& params)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_isCollapsed = false;
|
||||
std::shared_ptr<QtCodeSnippet> snippet(new QtCodeSnippet(params, m_navigator, this));
|
||||
|
||||
if (params.reduced)
|
||||
{
|
||||
m_title->setProject(params.title);
|
||||
m_isCollapsed = false;
|
||||
}
|
||||
|
||||
m_snippetLayout->addWidget(snippet.get());
|
||||
@@ -172,10 +171,6 @@ QtCodeSnippet* QtCodeFile::addCodeSnippet(const CodeSnippetParams& params)
|
||||
|
||||
return m_fileSnippet.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isCollapsed = false;
|
||||
}
|
||||
|
||||
m_snippets.push_back(snippet);
|
||||
|
||||
@@ -229,6 +224,11 @@ QtCodeSnippet* QtCodeFile::insertCodeSnippet(const CodeSnippetParams& params)
|
||||
|
||||
QtCodeSnippet* QtCodeFile::getSnippetForLocationId(Id locationId) const
|
||||
{
|
||||
if (m_fileSnippet && m_fileSnippet->isVisible() && m_fileSnippet->getLineNumberForLocationId(locationId))
|
||||
{
|
||||
return m_fileSnippet.get();
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<QtCodeSnippet>& snippet : m_snippets)
|
||||
{
|
||||
if (snippet->getLineNumberForLocationId(locationId))
|
||||
@@ -438,6 +438,23 @@ void QtCodeFile::updateTitleBar()
|
||||
m_title->updateTexts();
|
||||
}
|
||||
|
||||
void QtCodeFile::findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches)
|
||||
{
|
||||
if (m_fileSnippet && m_fileSnippet->isVisible())
|
||||
{
|
||||
m_fileSnippet->findScreenMatches(query, screenMatches);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<QtCodeSnippet>& snippet : m_snippets)
|
||||
{
|
||||
if (snippet->isVisible())
|
||||
{
|
||||
snippet->findScreenMatches(query, screenMatches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtCodeFile::clickedMinimizeButton()
|
||||
{
|
||||
// overview stats
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
class QtCodeArea;
|
||||
class QtCodeFileTitleButton;
|
||||
class QtCodeNavigator;
|
||||
class QtCodeSnippet;
|
||||
@@ -59,6 +60,8 @@ public:
|
||||
void updateSnippets();
|
||||
void updateTitleBar();
|
||||
|
||||
void findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches);
|
||||
|
||||
public slots:
|
||||
void clickedMinimizeButton();
|
||||
void clickedSnippetButton();
|
||||
|
||||
@@ -199,6 +199,14 @@ void QtCodeFileList::onWindowFocus()
|
||||
}
|
||||
}
|
||||
|
||||
void QtCodeFileList::findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches)
|
||||
{
|
||||
for (const std::shared_ptr<QtCodeFile>& filePtr : m_files)
|
||||
{
|
||||
filePtr->findScreenMatches(query, screenMatches);
|
||||
}
|
||||
}
|
||||
|
||||
void QtCodeFileList::setFileMinimized(const FilePath path)
|
||||
{
|
||||
getFile(path)->setMinimized();
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
|
||||
virtual void onWindowFocus();
|
||||
|
||||
virtual void findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches);
|
||||
|
||||
void setFileMinimized(const FilePath path);
|
||||
void setFileSnippets(const FilePath path);
|
||||
void setFileMaximized(const FilePath path);
|
||||
|
||||
@@ -225,6 +225,14 @@ void QtCodeFileSingle::onWindowFocus()
|
||||
m_title->updateTexts();
|
||||
}
|
||||
|
||||
void QtCodeFileSingle::findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches)
|
||||
{
|
||||
if (m_area)
|
||||
{
|
||||
m_area->findScreenMatches(query, screenMatches);
|
||||
}
|
||||
}
|
||||
|
||||
const FilePath& QtCodeFileSingle::getCurrentFilePath() const
|
||||
{
|
||||
return m_currentFilePath;
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
|
||||
virtual void onWindowFocus() override;
|
||||
|
||||
virtual void findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches) override;
|
||||
|
||||
const FilePath& getCurrentFilePath() const;
|
||||
bool hasFileCached(const FilePath& filePath) const;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
class FilePath;
|
||||
class QRectF;
|
||||
class QAbstractScrollArea;
|
||||
class QtCodeArea;
|
||||
class QWidget;
|
||||
|
||||
class QtCodeNavigateable
|
||||
@@ -29,6 +30,8 @@ public:
|
||||
|
||||
virtual void onWindowFocus() = 0;
|
||||
|
||||
virtual void findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches) = 0;
|
||||
|
||||
protected:
|
||||
void ensureWidgetVisibleAnimated(QWidget* parentWidget, QWidget *childWidget, QRectF rect, bool animated, bool onTop);
|
||||
void ensurePercentVisibleAnimated(double percentA, double percentB, bool animated, bool onTop);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "data/location/SourceLocation.h"
|
||||
#include "data/location/SourceLocationCollection.h"
|
||||
#include "data/location/SourceLocationFile.h"
|
||||
#include "qt/element/QtCodeArea.h"
|
||||
#include "qt/element/QtCodeFile.h"
|
||||
#include "qt/element/QtCodeSnippet.h"
|
||||
#include "qt/utility/utilityQt.h"
|
||||
@@ -255,6 +256,9 @@ void QtCodeNavigator::clearCodeSnippets()
|
||||
m_refIndex = 0;
|
||||
|
||||
m_singleHasNewFile = false;
|
||||
|
||||
m_screenMatches.clear();
|
||||
m_activeScreenMatchId = 0;
|
||||
}
|
||||
|
||||
void QtCodeNavigator::clearFile()
|
||||
@@ -549,6 +553,60 @@ void QtCodeNavigator::refreshStyle()
|
||||
clearCaches();
|
||||
}
|
||||
|
||||
size_t QtCodeNavigator::findScreenMatches(const std::string& query)
|
||||
{
|
||||
clearScreenMatches();
|
||||
|
||||
m_current->findScreenMatches(query, &m_screenMatches);
|
||||
|
||||
return m_screenMatches.size();
|
||||
}
|
||||
|
||||
void QtCodeNavigator::activateScreenMatch(size_t matchIndex)
|
||||
{
|
||||
if (matchIndex >= m_screenMatches.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::pair<QtCodeArea*, Id> p = m_screenMatches[matchIndex];
|
||||
m_activeScreenMatchId = p.second;
|
||||
m_currentActiveLocationIds.insert(m_activeScreenMatchId);
|
||||
p.first->updateContent();
|
||||
|
||||
requestScroll(p.first->getSourceLocationFile()->getFilePath(), 0, m_activeScreenMatchId, true, false);
|
||||
emit scrollRequest();
|
||||
}
|
||||
|
||||
void QtCodeNavigator::deactivateScreenMatch(size_t matchIndex)
|
||||
{
|
||||
if (matchIndex >= m_screenMatches.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_currentActiveLocationIds.erase(m_screenMatches[matchIndex].second);
|
||||
m_screenMatches[matchIndex].first->updateContent();
|
||||
|
||||
m_activeScreenMatchId = 0;
|
||||
}
|
||||
|
||||
void QtCodeNavigator::clearScreenMatches()
|
||||
{
|
||||
if (m_activeScreenMatchId)
|
||||
{
|
||||
m_currentActiveLocationIds.erase(m_activeScreenMatchId);
|
||||
m_activeScreenMatchId = 0;
|
||||
}
|
||||
|
||||
for (auto p : m_screenMatches)
|
||||
{
|
||||
p.first->clearScreenMatches();
|
||||
}
|
||||
|
||||
m_screenMatches.clear();
|
||||
}
|
||||
|
||||
void QtCodeNavigator::scrollToValue(int value, bool inListMode)
|
||||
{
|
||||
if ((m_mode == MODE_LIST) == inListMode)
|
||||
|
||||
@@ -83,6 +83,11 @@ public:
|
||||
|
||||
void refreshStyle();
|
||||
|
||||
size_t findScreenMatches(const std::string& query);
|
||||
void activateScreenMatch(size_t matchIndex);
|
||||
void deactivateScreenMatch(size_t matchIndex);
|
||||
void clearScreenMatches();
|
||||
|
||||
void scrollToValue(int value, bool inListMode);
|
||||
void scrollToLine(const FilePath& filePath, unsigned int line);
|
||||
void scrollToDefinition(bool animated, bool ignoreActiveReference);
|
||||
@@ -192,6 +197,9 @@ private:
|
||||
|
||||
ScrollRequest m_scrollRequest;
|
||||
bool m_singleHasNewFile;
|
||||
|
||||
std::vector<std::pair<QtCodeArea*, Id>> m_screenMatches;
|
||||
Id m_activeScreenMatchId = 0;
|
||||
};
|
||||
|
||||
#endif // QT_CODE_NAVIGATOR_H
|
||||
|
||||
@@ -187,6 +187,11 @@ std::string QtCodeSnippet::getCode() const
|
||||
return m_codeArea->getCode();
|
||||
}
|
||||
|
||||
void QtCodeSnippet::findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches)
|
||||
{
|
||||
m_codeArea->findScreenMatches(query, screenMatches);
|
||||
}
|
||||
|
||||
void QtCodeSnippet::clickedTitle()
|
||||
{
|
||||
if (m_titleId > 0)
|
||||
|
||||
@@ -51,6 +51,8 @@ public:
|
||||
|
||||
std::string getCode() const;
|
||||
|
||||
void findScreenMatches(const std::string& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches);
|
||||
|
||||
private slots:
|
||||
void clickedTitle();
|
||||
void clickedFooter();
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
#include "qt/element/QtScreenSearchBox.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCheckBox>
|
||||
#include <QFocusEvent>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QTimer>
|
||||
|
||||
#include "component/controller/helper/ControllerProxy.h"
|
||||
#include "component/controller/ScreenSearchController.h"
|
||||
#include "qt/utility/utilityQt.h"
|
||||
#include "utility/ResourcePaths.h"
|
||||
|
||||
|
||||
QtFocusInFilter::QtFocusInFilter()
|
||||
{
|
||||
}
|
||||
|
||||
bool QtFocusInFilter::eventFilter(QObject* obj, QEvent* event)
|
||||
{
|
||||
QLineEdit* lineEdit = dynamic_cast<QLineEdit*>(obj);
|
||||
if (lineEdit && event->type() == QEvent::FocusIn && dynamic_cast<QFocusEvent*>(event)->reason() == Qt::MouseFocusReason)
|
||||
{
|
||||
emit focusIn();
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
|
||||
QtScreenSearchBox::QtScreenSearchBox(ControllerProxy* controllerProxy, QWidget* parent)
|
||||
: QFrame(parent)
|
||||
, m_controllerProxy(controllerProxy)
|
||||
{
|
||||
setObjectName("screen_search_box");
|
||||
|
||||
QHBoxLayout* layout = new QHBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->setSpacing(0);
|
||||
setLayout(layout);
|
||||
|
||||
// search field
|
||||
{
|
||||
m_searchButton = new QPushButton();
|
||||
m_searchButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
|
||||
m_searchButton->setObjectName("search_button");
|
||||
layout->addWidget(m_searchButton);
|
||||
|
||||
connect(m_searchButton, &QPushButton::clicked, this, &QtScreenSearchBox::setFocus);
|
||||
|
||||
m_searchBox = new QLineEdit(this);
|
||||
m_searchBox->setObjectName("search_box");
|
||||
m_searchBox->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
|
||||
m_searchBox->setAttribute(Qt::WA_MacShowFocusRect, 0); // remove blue focus box on Mac
|
||||
layout->addWidget(m_searchBox);
|
||||
|
||||
connect(m_searchBox, &QLineEdit::textChanged, this, &QtScreenSearchBox::searchQueryChanged);
|
||||
connect(m_searchBox, &QLineEdit::returnPressed, this, &QtScreenSearchBox::returnPressed);
|
||||
|
||||
QtFocusInFilter* filter = new QtFocusInFilter();
|
||||
m_searchBox->installEventFilter(filter);
|
||||
connect(filter, &QtFocusInFilter::focusIn, this, &QtScreenSearchBox::findMatches);
|
||||
}
|
||||
|
||||
// match label
|
||||
{
|
||||
m_matchLabel = new QPushButton();
|
||||
m_matchLabel->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
|
||||
m_matchLabel->setObjectName("match_label");
|
||||
layout->addWidget(m_matchLabel);
|
||||
|
||||
connect(m_matchLabel, &QPushButton::clicked, this, &QtScreenSearchBox::setFocus);
|
||||
}
|
||||
|
||||
// buttons
|
||||
{
|
||||
m_prevButton = new QPushButton();
|
||||
m_nextButton = new QPushButton();
|
||||
|
||||
m_prevButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
|
||||
m_nextButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
|
||||
|
||||
m_prevButton->setObjectName("prev_button");
|
||||
m_nextButton->setObjectName("next_button");
|
||||
|
||||
layout->addWidget(m_prevButton);
|
||||
layout->addWidget(m_nextButton);
|
||||
|
||||
connect(m_prevButton, &QPushButton::clicked, this, &QtScreenSearchBox::previousPressed);
|
||||
connect(m_nextButton, &QPushButton::clicked, this, &QtScreenSearchBox::nextPressed);
|
||||
}
|
||||
|
||||
// filter
|
||||
{
|
||||
m_checkboxLayout = new QHBoxLayout();
|
||||
layout->addLayout(m_checkboxLayout);
|
||||
layout->addStretch();
|
||||
}
|
||||
|
||||
// buttons
|
||||
{
|
||||
m_closeButton = new QPushButton();
|
||||
m_closeButton->setObjectName("close_button");
|
||||
m_closeButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
|
||||
layout->addWidget(m_closeButton);
|
||||
|
||||
connect(m_closeButton, &QPushButton::clicked, [this](){ emit closePressed(); });
|
||||
}
|
||||
|
||||
m_timer = new QTimer(this);
|
||||
m_timer->setSingleShot(true);
|
||||
connect(m_timer, &QTimer::timeout, this, &QtScreenSearchBox::findMatches);
|
||||
|
||||
refreshStyle();
|
||||
setMatchCount(0);
|
||||
}
|
||||
|
||||
QtScreenSearchBox::~QtScreenSearchBox()
|
||||
{
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::refreshStyle()
|
||||
{
|
||||
m_searchButton->setIcon(utility::createButtonIcon(
|
||||
ResourcePaths::getGuiPath().str() + "search_view/images/search.png",
|
||||
"screen_search/button"
|
||||
));
|
||||
|
||||
m_prevButton->setIcon(utility::createButtonIcon(
|
||||
ResourcePaths::getGuiPath().str() + "code_view/images/arrow_left.png",
|
||||
"screen_search/button"
|
||||
));
|
||||
|
||||
m_nextButton->setIcon(utility::createButtonIcon(
|
||||
ResourcePaths::getGuiPath().str() + "code_view/images/arrow_right.png",
|
||||
"screen_search/button"
|
||||
));
|
||||
|
||||
m_closeButton->setIcon(utility::createButtonIcon(
|
||||
ResourcePaths::getGuiPath().str() + "screen_search_view/images/close.png",
|
||||
"screen_search/button"
|
||||
));
|
||||
|
||||
m_searchButton->setIconSize(QSize(12, 12));
|
||||
m_prevButton->setIconSize(QSize(12, 12));
|
||||
m_nextButton->setIconSize(QSize(12, 12));
|
||||
m_closeButton->setIconSize(QSize(15, 15));
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::setMatchCount(size_t matchCount)
|
||||
{
|
||||
m_matchCount = matchCount;
|
||||
m_matchIndex = 0;
|
||||
updateMatchLabel();
|
||||
|
||||
m_prevButton->setEnabled(matchCount > 0);
|
||||
m_nextButton->setEnabled(matchCount > 0);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::setMatchIndex(size_t matchIndex)
|
||||
{
|
||||
m_matchIndex = matchIndex;
|
||||
updateMatchLabel();
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::addResponder(const std::string& name)
|
||||
{
|
||||
QCheckBox* box = new QCheckBox(name.c_str());
|
||||
box->setObjectName("filter_checkbox");
|
||||
box->setChecked(true);
|
||||
m_checkBoxes.push_back(box);
|
||||
m_checkboxLayout->addWidget(box);
|
||||
|
||||
connect(box, &QCheckBox::stateChanged, this, &QtScreenSearchBox::findMatches);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::setFocus()
|
||||
{
|
||||
m_searchBox->setFocus();
|
||||
|
||||
if (m_searchBox->text().size())
|
||||
{
|
||||
m_searchBox->selectAll();
|
||||
searchQueryChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::searchQueryChanged()
|
||||
{
|
||||
m_timer->stop();
|
||||
m_timer->start(200);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::findMatches()
|
||||
{
|
||||
m_controllerProxy->executeAsTask<ScreenSearchController>(
|
||||
[this](ScreenSearchController* controller)
|
||||
{
|
||||
std::set<std::string> responderNames;
|
||||
for (QCheckBox* box : m_checkBoxes)
|
||||
{
|
||||
if (box->isChecked())
|
||||
{
|
||||
responderNames.insert(box->text().toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
controller->search(m_searchBox->text().toLower().toStdString(), responderNames);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::returnPressed()
|
||||
{
|
||||
if (Qt::KeyboardModifier::ShiftModifier & QApplication::keyboardModifiers())
|
||||
{
|
||||
previousPressed();
|
||||
}
|
||||
else
|
||||
{
|
||||
nextPressed();
|
||||
}
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::previousPressed()
|
||||
{
|
||||
activateMatch(false);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::nextPressed()
|
||||
{
|
||||
activateMatch(true);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::activateMatch(bool next)
|
||||
{
|
||||
m_controllerProxy->executeAsTask<ScreenSearchController>(
|
||||
[next, this](ScreenSearchController* controller)
|
||||
{
|
||||
controller->activateMatch(next);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void QtScreenSearchBox::updateMatchLabel()
|
||||
{
|
||||
QString text;
|
||||
|
||||
if (m_matchIndex > 0)
|
||||
{
|
||||
text += QString::number(m_matchIndex) + " of ";
|
||||
}
|
||||
|
||||
text += QString::number(m_matchCount) + " match";
|
||||
if (m_matchCount != 1)
|
||||
{
|
||||
text += "es";
|
||||
}
|
||||
|
||||
m_matchLabel->setText(text);
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#ifndef QT_SCREEN_SEARCH_BOX_H
|
||||
#define QT_SCREEN_SEARCH_BOX_H
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
class ControllerProxy;
|
||||
class QCheckBox;
|
||||
class QHBoxLayout;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QTimer;
|
||||
|
||||
|
||||
class QtFocusInFilter
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtFocusInFilter();
|
||||
|
||||
signals:
|
||||
void focusIn();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* event);
|
||||
};
|
||||
|
||||
|
||||
class QtScreenSearchBox
|
||||
: public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtScreenSearchBox(ControllerProxy* controllerProxy, QWidget* parent = nullptr);
|
||||
virtual ~QtScreenSearchBox();
|
||||
|
||||
void refreshStyle();
|
||||
|
||||
void setMatchCount(size_t matchCount);
|
||||
void setMatchIndex(size_t matchIndex);
|
||||
|
||||
void addResponder(const std::string& name);
|
||||
|
||||
signals:
|
||||
void closePressed();
|
||||
|
||||
public slots:
|
||||
void setFocus();
|
||||
|
||||
private slots:
|
||||
void searchQueryChanged();
|
||||
void findMatches();
|
||||
void returnPressed();
|
||||
|
||||
void previousPressed();
|
||||
void nextPressed();
|
||||
|
||||
private:
|
||||
void activateMatch(bool next);
|
||||
|
||||
void updateMatchLabel();
|
||||
|
||||
ControllerProxy* m_controllerProxy;
|
||||
|
||||
QLineEdit* m_searchBox;
|
||||
QPushButton* m_matchLabel;
|
||||
|
||||
QPushButton* m_searchButton;
|
||||
QPushButton* m_prevButton;
|
||||
QPushButton* m_nextButton;
|
||||
QPushButton* m_closeButton;
|
||||
|
||||
QHBoxLayout* m_checkboxLayout;
|
||||
std::vector<QCheckBox*> m_checkBoxes;
|
||||
|
||||
size_t m_matchCount = 0;
|
||||
size_t m_matchIndex = 0;
|
||||
|
||||
QTimer* m_timer;
|
||||
};
|
||||
|
||||
#endif // QT_SCREEN_SEARCH_BOX_H
|
||||
Reference in New Issue
Block a user