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:
Eberhard Graether
2017-09-19 14:00:40 +02:00
parent 4731f379f4
commit 95ef8c3eec
64 changed files with 1642 additions and 174 deletions
+46
View File
@@ -45,6 +45,52 @@ void QtCodeView::refreshView()
});
}
bool QtCodeView::isVisible() const
{
return m_widget->isVisible();
}
void QtCodeView::findMatches(ScreenSearchSender* sender, const std::string& query)
{
m_onQtThread(
[sender, query, this]()
{
size_t matchCount = m_widget->findScreenMatches(query);
sender->foundMatches(this, matchCount);
}
);
}
void QtCodeView::activateMatch(size_t matchIndex)
{
m_onQtThread(
[matchIndex, this]()
{
m_widget->activateScreenMatch(matchIndex);
}
);
}
void QtCodeView::deactivateMatch(size_t matchIndex)
{
m_onQtThread(
[matchIndex, this]()
{
m_widget->deactivateScreenMatch(matchIndex);
}
);
}
void QtCodeView::clearMatches()
{
m_onQtThread(
[this]()
{
m_widget->clearScreenMatches();
}
);
}
void QtCodeView::clear()
{
m_onQtThread([=]()
+7
View File
@@ -18,6 +18,13 @@ public:
virtual void initView();
virtual void refreshView();
// ScreenSearchResponder implementation
virtual bool isVisible() const;
virtual void findMatches(ScreenSearchSender* sender, const std::string& query);
virtual void activateMatch(size_t matchIndex);
virtual void deactivateMatch(size_t matchIndex);
virtual void clearMatches();
// CodeView implementation
virtual void clear();
+78
View File
@@ -168,6 +168,80 @@ void QtGraphView::refreshView()
getView()->refreshStyle();
}
bool QtGraphView::isVisible() const
{
return QtViewWidgetWrapper::getWidgetOfView(this)->isVisible();
}
void QtGraphView::findMatches(ScreenSearchSender* sender, const std::string& query)
{
m_onQtThread(
[sender, query, this]()
{
m_matchedNodes.clear();
for (std::shared_ptr<QtGraphNode> node : m_oldNodes)
{
node->matchNameRecursive(query, &m_matchedNodes);
}
sender->foundMatches(this, m_matchedNodes.size());
}
);
}
void QtGraphView::activateMatch(size_t matchIndex)
{
m_onQtThread(
[matchIndex, this]()
{
if (matchIndex >= m_matchedNodes.size())
{
return;
}
QtGraphNode* node = m_matchedNodes[matchIndex];
node->setActiveMatch(true);
node->updateStyle();
centerNode(node);
}
);
}
void QtGraphView::deactivateMatch(size_t matchIndex)
{
m_onQtThread(
[matchIndex, this]()
{
if (matchIndex >= m_matchedNodes.size())
{
return;
}
QtGraphNode* node = m_matchedNodes[matchIndex];
node->setActiveMatch(false);
node->updateStyle();
}
);
}
void QtGraphView::clearMatches()
{
m_onQtThread(
[this]()
{
for (QtGraphNode* node : m_matchedNodes)
{
node->removeNameMatch();
}
m_matchedNodes.clear();
}
);
}
void QtGraphView::rebuildGraph(
std::shared_ptr<Graph> graph,
const std::vector<std::shared_ptr<DummyNode>>& nodes,
@@ -599,6 +673,8 @@ void QtGraphView::doRebuildGraph(
m_graph = graph;
}
m_matchedNodes.clear();
QGraphicsView* view = getView();
@@ -682,6 +758,8 @@ void QtGraphView::doClear()
m_graph.reset();
m_oldGraph.reset();
m_matchedNodes.clear();
}
void QtGraphView::doResize()
+11
View File
@@ -39,6 +39,14 @@ public:
virtual void initView();
virtual void refreshView();
// ScreenSearchResponder implementation
virtual bool isVisible() const;
virtual void findMatches(ScreenSearchSender* sender, const std::string& query);
virtual void activateMatch(size_t matchIndex);
virtual void deactivateMatch(size_t matchIndex);
virtual void clearMatches();
// GraphView implementation
virtual void rebuildGraph(
std::shared_ptr<Graph> graph,
const std::vector<std::shared_ptr<DummyNode>>& nodes,
@@ -164,6 +172,9 @@ private:
QLabel* m_trailDepthLabel;
std::vector<QRectF> m_virtualNodeRects;
// Name matches
std::vector<QtGraphNode*> m_matchedNodes;
};
#endif // QT_GRAPH_VIEW_H
@@ -0,0 +1,98 @@
#include "qt/view/QtScreenSearchView.h"
#include <QToolBar>
#include "component/controller/ScreenSearchController.h"
#include "qt/element/QtScreenSearchBox.h"
#include "qt/utility/utilityQt.h"
#include "qt/view/QtMainView.h"
#include "qt/view/QtViewWidgetWrapper.h"
#include "qt/window/QtMainWindow.h"
#include "utility/ResourcePaths.h"
QtScreenSearchView::QtScreenSearchView(ViewLayout* viewLayout)
: ScreenSearchView(viewLayout)
, m_controllerProxy(this)
{
m_widget = new QtScreenSearchBox(&m_controllerProxy);
m_bar = new QToolBar();
m_bar->setObjectName("screen_search_bar");
m_bar->setMovable(false);
m_bar->addWidget(m_widget);
QtMainWindow* mainWindow = dynamic_cast<QtMainView*>(getViewLayout())->getMainWindow();
QObject::connect(m_widget, &QtScreenSearchBox::closePressed, this, &QtScreenSearchView::hide);
QObject::connect(mainWindow, &QtMainWindow::showScreenSearch, this, &QtScreenSearchView::show);
QObject::connect(mainWindow, &QtMainWindow::hideScreenSearch, this, &QtScreenSearchView::hide);
}
QtScreenSearchView::~QtScreenSearchView()
{
}
void QtScreenSearchView::createWidgetWrapper()
{
setWidgetWrapper(std::make_shared<QtViewWidgetWrapper>(m_widget));
}
void QtScreenSearchView::initView()
{
}
void QtScreenSearchView::refreshView()
{
m_onQtThread([=]()
{
m_bar->setStyleSheet(
utility::getStyleSheet(ResourcePaths::getGuiPath().concat(
FilePath("screen_search_view/screen_search_view.css"))).c_str()
);
m_widget->refreshStyle();
});
}
void QtScreenSearchView::setMatchCount(size_t matchCount)
{
m_onQtThread([=]()
{
m_widget->setMatchCount(matchCount);
});
}
void QtScreenSearchView::setMatchIndex(size_t matchIndex)
{
m_onQtThread([=]()
{
m_widget->setMatchIndex(matchIndex);
});
}
void QtScreenSearchView::addResponder(const std::string& name)
{
m_widget->addResponder(name);
}
void QtScreenSearchView::show()
{
QtMainWindow* mainWindow = dynamic_cast<QtMainView*>(getViewLayout())->getMainWindow();
mainWindow->addToolBar(Qt::BottomToolBarArea, m_bar);
m_bar->show();
m_widget->setFocus();
}
void QtScreenSearchView::hide()
{
m_bar->hide();
m_widget->setMatchCount(0);
m_controllerProxy.executeAsTask<ScreenSearchController>(
[](ScreenSearchController* controller)
{
controller->clearMatches();
}
);
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef QT_SCREEN_SEARCH_VIEW_H
#define QT_SCREEN_SEARCH_VIEW_H
#include "component/controller/helper/ControllerProxy.h"
#include "component/view/ScreenSearchView.h"
#include "qt/utility/QtThreadedFunctor.h"
class QtScreenSearchBox;
class QToolBar;
class QtScreenSearchView
: public QObject
, public ScreenSearchView
{
Q_OBJECT
public:
QtScreenSearchView(ViewLayout* viewLayout);
~QtScreenSearchView();
// View implementation
virtual void createWidgetWrapper() override;
virtual void initView() override;
virtual void refreshView() override;
// ScreenSearchView implementation
virtual void setMatchCount(size_t matchCount) override;
virtual void setMatchIndex(size_t matchIndex) override;
virtual void addResponder(const std::string& name) override;
public slots:
void show();
void hide();
private:
ControllerProxy m_controllerProxy;
QtThreadedLambdaFunctor m_onQtThread;
QtScreenSearchBox* m_widget;
QToolBar* m_bar;
};
#endif // QT_SCREEN_SEARCH_VIEW_H
+4 -10
View File
@@ -5,13 +5,12 @@
#include "qt/view/QtMainView.h"
#include "qt/view/QtViewWidgetWrapper.h"
#include "qt/window/QtMainWindow.h"
#include "settings/ColorScheme.h"
#include "utility/ResourcePaths.h"
QtTooltipView::QtTooltipView(ViewLayout* viewLayout)
: TooltipView(viewLayout)
{
m_widget = new QtTooltip(dynamic_cast<QtMainView*>(getViewLayout())->getMainWindow());
m_widget = new QtTooltip(dynamic_cast<QtMainView*>(viewLayout)->getMainWindow());
}
QtTooltipView::~QtTooltipView()
@@ -31,7 +30,9 @@ void QtTooltipView::refreshView()
{
m_onQtThread([=]()
{
setStyleSheet();
m_widget->setStyleSheet(
utility::getStyleSheet(ResourcePaths::getGuiPath().concat(FilePath("tooltip_view/tooltip_view.css"))).c_str()
);
});
}
@@ -68,10 +69,3 @@ bool QtTooltipView::tooltipVisible() const
{
return m_widget->isVisible();
}
void QtTooltipView::setStyleSheet()
{
m_widget->setStyleSheet(
utility::getStyleSheet(ResourcePaths::getGuiPath().concat(FilePath("tooltip_view/tooltip_view.css"))).c_str()
);
}
-3
View File
@@ -25,9 +25,6 @@ public:
virtual bool tooltipVisible() const;
private:
void setStyleSheet();
void doRefreshView();
QtThreadedLambdaFunctor m_onQtThread;
QtTooltip* m_widget;
+6
View File
@@ -11,6 +11,7 @@
#include "qt/view/QtLogView.h"
#include "qt/view/QtMainView.h"
#include "qt/view/QtRefreshView.h"
#include "qt/view/QtScreenSearchView.h"
#include "qt/view/QtSearchView.h"
#include "qt/view/QtStatusBarView.h"
#include "qt/view/QtStatusView.h"
@@ -84,6 +85,11 @@ std::shared_ptr<RefreshView> QtViewFactory::createRefreshView(ViewLayout* viewLa
return View::createInitAndAddToLayout<QtRefreshView>(viewLayout);
}
std::shared_ptr<ScreenSearchView> QtViewFactory::createScreenSearchView(ViewLayout* viewLayout) const
{
return View::createAndInit<QtScreenSearchView>(viewLayout);
}
std::shared_ptr<SearchView> QtViewFactory::createSearchView(ViewLayout* viewLayout) const
{
return View::createInitAndAddToLayout<QtSearchView>(viewLayout);
+1
View File
@@ -20,6 +20,7 @@ public:
virtual std::shared_ptr<GraphView> createGraphView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<LogView> createLogView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<RefreshView> createRefreshView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<ScreenSearchView> createScreenSearchView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<SearchView> createSearchView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<StatusBarView> createStatusBarView(ViewLayout* viewLayout) const;
virtual std::shared_ptr<StatusView> createStatusView(ViewLayout* viewLayout) const;
@@ -2,6 +2,7 @@
#include <QBrush>
#include <QFont>
#include <QFontMetrics>
#include <QGraphicsSceneEvent>
#include <QPen>
@@ -11,6 +12,7 @@
#include "qt/view/graphElements/nodeComponents/QtGraphNodeComponent.h"
#include "qt/view/graphElements/QtGraphEdge.h"
#include "utility/ResourcePaths.h"
#include "utility/utilityString.h"
void QtGraphNode::blendIn()
{
@@ -40,10 +42,6 @@ QFont QtGraphNode::getFontForNodeType(Node::NodeType type)
}
QtGraphNode::QtGraphNode()
: m_icon(nullptr)
, m_isActive(false)
, m_multipleActive(false)
, m_isHovering(false)
{
this->setPen(QPen(Qt::transparent));
@@ -244,6 +242,35 @@ void QtGraphNode::focusOut()
updateStyle();
}
void QtGraphNode::matchNameRecursive(const std::string& query, std::vector<QtGraphNode*>* matchedNodes)
{
matchName(query, matchedNodes);
for (auto subNode : m_subNodes)
{
subNode->matchNameRecursive(query, matchedNodes);
}
}
void QtGraphNode::removeNameMatch()
{
if (m_matchLength)
{
m_matchRect->hide();
m_matchText->hide();
m_matchPos = 0;
m_matchLength = 0;
updateStyle();
}
}
void QtGraphNode::setActiveMatch(bool active)
{
m_isActiveMatch = active;
}
bool QtGraphNode::isDataNode() const
{
return false;
@@ -407,6 +434,40 @@ void QtGraphNode::notifyEdgesAfterMove()
}
}
void QtGraphNode::matchName(const std::string& query, std::vector<QtGraphNode*>* matchedNodes)
{
m_isActiveMatch = false;
std::string name = getName();
size_t pos = utility::toLowerCase(name).find(query);
if (pos != std::string::npos)
{
if (!m_matchText)
{
m_matchRect = new QtRoundedRectItem(this);
m_matchText = new QGraphicsSimpleTextItem(this);
}
m_matchRect->show();
m_matchText->show();
std::string matchName(name.length(), ' ');
matchName.replace(pos, query.length(), name.substr(pos, query.length()));
m_matchText->setText(matchName.c_str());
matchedNodes->push_back(this);
m_matchPos = pos;
m_matchLength = query.length();
updateStyle();
}
else if (m_matchLength)
{
removeNameMatch();
}
}
void QtGraphNode::setStyle(const GraphViewStyle::NodeStyle& style)
{
QPen pen(Qt::transparent);
@@ -462,4 +523,25 @@ void QtGraphNode::setStyle(const GraphViewStyle::NodeStyle& style)
m_text->setFont(font);
m_text->setBrush(QBrush(style.color.text.c_str()));
m_text->setPos(style.iconOffset.x + style.iconSize + style.textOffset.x, style.textOffset.y);
if (m_matchLength)
{
GraphViewStyle::NodeColor color = GraphViewStyle::getScreenMatchColor(m_isActiveMatch);
m_matchText->setFont(font);
m_matchText->setBrush(QBrush(color.text.c_str()));
m_matchText->setPos(style.iconOffset.x + style.iconSize + style.textOffset.x, style.textOffset.y);
float charWidth = QFontMetrics(font).width("QtGraphNode::QtGraphNode::QtGraphNode") / 37.0f;
float charHeight = QFontMetrics(font).height();
m_matchRect->setRect(
style.iconOffset.x + style.iconSize + style.textOffset.x + m_matchPos * charWidth,
style.textOffset.y,
m_matchLength * charWidth,
charHeight
);
m_matchRect->setPen(QPen(color.border.c_str()));
m_matchRect->setBrush(QBrush(color.fill.c_str()));
m_matchRect->setRadius(3);
}
}
@@ -74,6 +74,10 @@ public:
void focusIn();
void focusOut();
void matchNameRecursive(const std::string& query, std::vector<QtGraphNode*>* matchedNodes);
void removeNameMatch();
void setActiveMatch(bool active);
virtual bool isDataNode() const;
virtual bool isAccessNode() const;
virtual bool isExpandToggleNode() const;
@@ -99,6 +103,8 @@ protected:
void notifyEdgesAfterMove();
virtual void matchName(const std::string& query, std::vector<QtGraphNode*>* matchedNodes);
void setStyle(const GraphViewStyle::NodeStyle& style);
std::list<std::shared_ptr<QtGraphEdge>> m_outEdges;
@@ -107,19 +113,26 @@ protected:
std::weak_ptr<QtGraphNode> m_parentNode;
std::list<std::shared_ptr<QtGraphNode>> m_subNodes;
QGraphicsSimpleTextItem* m_text;
QtRoundedRectItem* m_rect;
QtRoundedRectItem* m_undefinedRect;
QGraphicsPixmapItem* m_icon;
QGraphicsSimpleTextItem* m_text = nullptr;
QtRoundedRectItem* m_rect = nullptr;
QtRoundedRectItem* m_undefinedRect = nullptr;
QGraphicsPixmapItem* m_icon = nullptr;
Vec2i m_size;
bool m_isActive;
bool m_multipleActive;
bool m_isHovering;
bool m_isActive = false;
bool m_multipleActive = false;
bool m_isHovering = false;
private:
std::list<std::shared_ptr<QtGraphNodeComponent>> m_components;
// Name match
QGraphicsSimpleTextItem* m_matchText = nullptr;
QtRoundedRectItem* m_matchRect = nullptr;
size_t m_matchPos = 0;
size_t m_matchLength = 0;
bool m_isActiveMatch = false;
};
#endif // QT_GRAPH_NODE_H
@@ -23,6 +23,9 @@ public:
void hideLabel();
protected:
virtual void matchName(const std::string& query, std::vector<QtGraphNode*>* matchedNodes) {}
private:
AccessKind m_accessKind;
@@ -19,6 +19,9 @@ public:
virtual void onClick();
virtual void updateStyle();
protected:
virtual void matchName(const std::string& query, std::vector<QtGraphNode*>* matchedNodes) {}
private:
QGraphicsPixmapItem* m_icon;
bool m_invisibleSubNodeCount;
@@ -26,6 +26,8 @@ protected:
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
virtual void matchName(const std::string& query, std::vector<QtGraphNode*>* matchedNodes) {}
private:
const NameHierarchy m_qualifierName;