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
+5
View File
@@ -10,6 +10,7 @@ add_files(
component/controller/helper/ListLayouter.h
component/controller/helper/NetworkProtocolHelper.cpp
component/controller/helper/NetworkProtocolHelper.h
component/controller/helper/ScreenSearchInterfaces.h
component/controller/helper/SnippetMerger.cpp
component/controller/helper/SnippetMerger.h
component/controller/helper/TrailLayouter.cpp
@@ -32,6 +33,8 @@ add_files(
component/controller/LogController.h
component/controller/RefreshController.cpp
component/controller/RefreshController.h
component/controller/ScreenSearchController.cpp
component/controller/ScreenSearchController.h
component/controller/SearchController.cpp
component/controller/SearchController.h
component/controller/StatusBarController.cpp
@@ -66,6 +69,8 @@ add_files(
component/view/MainView.h
component/view/RefreshView.cpp
component/view/RefreshView.h
component/view/ScreenSearchView.cpp
component/view/ScreenSearchView.h
component/view/SearchView.cpp
component/view/SearchView.h
component/view/StatusBarView.cpp
+10
View File
@@ -30,3 +30,13 @@ Component::~Component()
m_view->setComponent(NULL);
}
}
Controller* Component::getControllerPtr() const
{
return m_controller.get();
}
View* Component::getViewPtr() const
{
return m_view.get();
}
+4
View File
@@ -15,9 +15,13 @@ public:
template <typename ControllerType>
ControllerType* getController() const;
Controller* getControllerPtr() const;
template <typename ViewType>
ViewType* getView() const;
View* getViewPtr() const;
private:
const std::shared_ptr<Controller> m_controller;
const std::shared_ptr<View> m_view;
+32 -22
View File
@@ -8,6 +8,7 @@
#include "component/controller/GraphController.h"
#include "component/controller/LogController.h"
#include "component/controller/RefreshController.h"
#include "component/controller/ScreenSearchController.h"
#include "component/controller/SearchController.h"
#include "component/controller/StatusBarController.h"
#include "component/controller/StatusController.h"
@@ -18,6 +19,7 @@
#include "component/view/ErrorView.h"
#include "component/view/LogView.h"
#include "component/view/RefreshView.h"
#include "component/view/ScreenSearchView.h"
#include "component/view/SearchView.h"
#include "component/view/StatusBarView.h"
#include "component/view/StatusView.h"
@@ -58,14 +60,6 @@ std::shared_ptr<Component> ComponentFactory::createActivationComponent()
return std::make_shared<Component>(nullptr, controller);
}
std::shared_ptr<Component> ComponentFactory::createTooltipComponent(ViewLayout* viewLayout)
{
std::shared_ptr<TooltipView> view = m_viewFactory->createTooltipView(viewLayout);
std::shared_ptr<Controller> controller = std::make_shared<TooltipController>(m_storageAccess);
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createBookmarkComponent(ViewLayout* viewLayout)
{
std::shared_ptr<BookmarkView> view = m_viewFactory->createBookmarkView(viewLayout);
@@ -90,6 +84,14 @@ std::shared_ptr<Component> ComponentFactory::createErrorComponent(ViewLayout* vi
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createGraphComponent(ViewLayout* viewLayout)
{
std::shared_ptr<View> view = m_viewFactory->createGraphView(viewLayout);
std::shared_ptr<GraphController> controller = std::make_shared<GraphController>(m_storageAccess);
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createLogComponent(ViewLayout* viewLayout)
{
std::shared_ptr<LogView> view = m_viewFactory->createLogView(viewLayout);
@@ -100,14 +102,6 @@ std::shared_ptr<Component> ComponentFactory::createLogComponent(ViewLayout* view
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createGraphComponent(ViewLayout* viewLayout)
{
std::shared_ptr<View> view = m_viewFactory->createGraphView(viewLayout);
std::shared_ptr<GraphController> controller = std::make_shared<GraphController>(m_storageAccess);
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createRefreshComponent(ViewLayout* viewLayout)
{
std::shared_ptr<View> view = m_viewFactory->createRefreshView(viewLayout);
@@ -116,18 +110,18 @@ std::shared_ptr<Component> ComponentFactory::createRefreshComponent(ViewLayout*
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createSearchComponent(ViewLayout* viewLayout)
std::shared_ptr<Component> ComponentFactory::createScreenSearchComponent(ViewLayout* viewLayout)
{
std::shared_ptr<SearchView> view = m_viewFactory->createSearchView(viewLayout);
std::shared_ptr<SearchController> controller = std::make_shared<SearchController>(m_storageAccess);
std::shared_ptr<ScreenSearchView> view = m_viewFactory->createScreenSearchView(viewLayout);
std::shared_ptr<ScreenSearchController> controller = std::make_shared<ScreenSearchController>();
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createUndoRedoComponent(ViewLayout* viewLayout)
std::shared_ptr<Component> ComponentFactory::createSearchComponent(ViewLayout* viewLayout)
{
std::shared_ptr<UndoRedoView> view = m_viewFactory->createUndoRedoView(viewLayout);
std::shared_ptr<UndoRedoController> controller = std::make_shared<UndoRedoController>(m_storageAccess);
std::shared_ptr<SearchView> view = m_viewFactory->createSearchView(viewLayout);
std::shared_ptr<SearchController> controller = std::make_shared<SearchController>(m_storageAccess);
return std::make_shared<Component>(view, controller);
}
@@ -148,6 +142,22 @@ std::shared_ptr<Component> ComponentFactory::createStatusComponent(ViewLayout* v
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createTooltipComponent(ViewLayout* viewLayout)
{
std::shared_ptr<TooltipView> view = m_viewFactory->createTooltipView(viewLayout);
std::shared_ptr<Controller> controller = std::make_shared<TooltipController>(m_storageAccess);
return std::make_shared<Component>(view, controller);
}
std::shared_ptr<Component> ComponentFactory::createUndoRedoComponent(ViewLayout* viewLayout)
{
std::shared_ptr<UndoRedoView> view = m_viewFactory->createUndoRedoView(viewLayout);
std::shared_ptr<UndoRedoController> controller = std::make_shared<UndoRedoController>(m_storageAccess);
return std::make_shared<Component>(view, controller);
}
ComponentFactory::ComponentFactory()
{
}
+1
View File
@@ -26,6 +26,7 @@ public:
std::shared_ptr<Component> createGraphComponent(ViewLayout* viewLayout);
std::shared_ptr<Component> createLogComponent(ViewLayout* viewLayout);
std::shared_ptr<Component> createRefreshComponent(ViewLayout* viewLayout);
std::shared_ptr<Component> createScreenSearchComponent(ViewLayout* viewLayout);
std::shared_ptr<Component> createSearchComponent(ViewLayout* viewLayout);
std::shared_ptr<Component> createStatusBarComponent(ViewLayout* viewLayout);
std::shared_ptr<Component> createStatusComponent(ViewLayout* viewLayout);
+7
View File
@@ -1,6 +1,7 @@
#include "component/ComponentManager.h"
#include "component/controller/Controller.h"
#include "component/controller/ScreenSearchController.h"
#include "component/view/CompositeView.h"
#include "component/view/DialogView.h"
#include "component/view/TabbedView.h"
@@ -52,6 +53,12 @@ void ComponentManager::setup(ViewLayout* viewLayout)
std::shared_ptr<Component> tooltipComponent = m_componentFactory->createTooltipComponent(viewLayout);
m_components.push_back(tooltipComponent);
std::shared_ptr<Component> screenSearchComponent = m_componentFactory->createScreenSearchComponent(viewLayout);
ScreenSearchController* screenSearchController = screenSearchComponent->getController<ScreenSearchController>();
screenSearchController->addResponder(dynamic_cast<ScreenSearchResponder*>(graphComponent->getViewPtr()));
screenSearchController->addResponder(dynamic_cast<ScreenSearchResponder*>(codeComponent->getViewPtr()));
m_components.push_back(screenSearchComponent);
m_dialogView = m_componentFactory->getViewFactory()->createDialogView(viewLayout, m_componentFactory->getStorageAccess());
std::shared_ptr<TabbedView> tabbedView =
@@ -0,0 +1,153 @@
#include "component/controller/ScreenSearchController.h"
#include "component/view/ScreenSearchView.h"
ScreenSearchController::ScreenSearchController()
{
}
ScreenSearchController::~ScreenSearchController()
{
}
void ScreenSearchController::clear()
{
}
void ScreenSearchController::foundMatches(ScreenSearchResponder* responder, size_t matchCount)
{
if (matchCount)
{
std::lock_guard<std::mutex> lock(m_matchMutex);
size_t responderId = getResponderId(responder);
if (!responderId)
{
return;
}
std::vector<std::pair<size_t, size_t>> newMatches;
for (size_t i = 0; i < matchCount; i++)
{
newMatches.push_back(std::make_pair(responderId, i));
}
size_t i = 0;
while (i < m_matches.size() && responderId > m_matches[i].first)
{
i++;
}
m_matches.insert(m_matches.begin() + i, newMatches.begin(), newMatches.end());
m_matchIndex = m_matches.size();
}
getView<ScreenSearchView>()->setMatchCount(m_matches.size());
}
void ScreenSearchController::addResponder(ScreenSearchResponder* responder)
{
if (responder)
{
m_responders.push_back(responder);
getView<ScreenSearchView>()->addResponder(responder->getName());
}
}
void ScreenSearchController::search(const std::string& query, const std::set<std::string>& responderNames)
{
{
std::lock_guard<std::mutex> lock(m_matchMutex);
m_matches.clear();
m_matchIndex = 0;
}
getView<ScreenSearchView>()->setMatchCount(0);
for (ScreenSearchResponder* responder : m_responders)
{
if (!responder->isVisible())
{
continue;
}
if (query.size() && responderNames.find(responder->getName()) != responderNames.end())
{
responder->findMatches(this, query);
}
else
{
responder->clearMatches();
}
}
}
void ScreenSearchController::activateMatch(bool next)
{
std::lock_guard<std::mutex> lock(m_matchMutex);
if (!m_matches.size())
{
return;
}
if (m_matchIndex != m_matches.size())
{
auto match = m_matches[m_matchIndex];
m_responders[match.first - 1]->deactivateMatch(match.second);
}
if (next)
{
if (m_matchIndex == m_matches.size())
{
m_matchIndex = 0;
}
else
{
m_matchIndex = (m_matchIndex + 1) % m_matches.size();
}
}
else
{
if (m_matchIndex == 0)
{
m_matchIndex = m_matches.size() - 1;
}
else
{
m_matchIndex--;
}
}
auto match = m_matches[m_matchIndex];
m_responders[match.first - 1]->activateMatch(match.second);
getView<ScreenSearchView>()->setMatchIndex(m_matchIndex + 1);
}
void ScreenSearchController::clearMatches()
{
{
std::lock_guard<std::mutex> lock(m_matchMutex);
m_matches.clear();
m_matchIndex = 0;
}
for (ScreenSearchResponder* responder : m_responders)
{
responder->clearMatches();
}
}
size_t ScreenSearchController::getResponderId(ScreenSearchResponder* responder) const
{
for (size_t i = 0; i < m_responders.size(); i++)
{
if (m_responders[i] == responder)
{
return i + 1;
}
}
return 0;
}
@@ -0,0 +1,40 @@
#ifndef SCREEN_SEARCH_CONTROLLER_H
#define SCREEN_SEARCH_CONTROLLER_H
#include <mutex>
#include <set>
#include "component/controller/Controller.h"
#include "component/controller/helper/ScreenSearchInterfaces.h"
#include "utility/messaging/MessageListener.h"
class ScreenSearchController
: public Controller
, public ScreenSearchSender
{
public:
ScreenSearchController();
virtual ~ScreenSearchController();
// Controller implementation
virtual void clear();
// ScreenSearchSender implementation
virtual void foundMatches(ScreenSearchResponder* responder, size_t matchCount);
void addResponder(ScreenSearchResponder* responder);
void search(const std::string& query, const std::set<std::string>& responderNames);
void activateMatch(bool next);
void clearMatches();
private:
size_t getResponderId(ScreenSearchResponder* responder) const;
std::vector<ScreenSearchResponder*> m_responders;
std::vector<std::pair<size_t, size_t>> m_matches;
size_t m_matchIndex = 0;
std::mutex m_matchMutex;
};
#endif // SCREEN_SEARCH_CONTROLLER_H
@@ -0,0 +1,47 @@
#ifndef CONTROLLER_PROXY_H
#define CONTROLLER_PROXY_H
#include <functional>
#include "utility/scheduling/TaskLambda.h"
#include "component/view/View.h"
class ControllerProxy
{
public:
ControllerProxy(View* view)
: m_view(view)
{
}
template<typename ControllerType>
void execute(std::function<void(ControllerType*)> callback)
{
ControllerType* controller = m_view->getController<ControllerType>();
if (controller)
{
callback(controller);
}
}
template<typename ControllerType>
void executeAsTask(std::function<void(ControllerType*)> callback)
{
ControllerType* controller = m_view->getController<ControllerType>();
if (controller)
{
Task::dispatch(std::make_shared<TaskLambda>(
[callback, controller]()
{
callback(controller);
}
));
}
}
private:
View* m_view;
};
#endif // CONTROLLER_PROXY_H
@@ -0,0 +1,28 @@
#ifndef SCREEN_SEARCH_INTERFACES_H
#define SCREEN_SEARCH_INTERFACES_H
class ScreenSearchResponder;
class ScreenSearchSender
{
public:
virtual ~ScreenSearchSender() {}
virtual void foundMatches(ScreenSearchResponder* responder, size_t matchCount) = 0;
};
class ScreenSearchResponder
{
public:
virtual ~ScreenSearchResponder() {}
virtual std::string getName() const = 0;
virtual bool isVisible() const = 0;
virtual void findMatches(ScreenSearchSender* sender, const std::string& query) = 0;
virtual void activateMatch(size_t matchIndex) = 0;
virtual void deactivateMatch(size_t matchIndex) = 0;
virtual void clearMatches() = 0;
};
#endif // SCREEN_SEARCH_INTERFACES_H
+2
View File
@@ -5,6 +5,7 @@
#include "data/ErrorInfo.h"
#include "component/controller/helper/ScreenSearchInterfaces.h"
#include "component/view/helper/CodeSnippetParams.h"
#include "component/view/View.h"
@@ -14,6 +15,7 @@ class SourceLocationCollection;
class CodeView
: public View
, public ScreenSearchResponder
{
public:
static const char* VIEW_NAME;
+2
View File
@@ -6,6 +6,7 @@
#include "utility/math/Vector2.h"
#include "utility/types.h"
#include "component/controller/helper/ScreenSearchInterfaces.h"
#include "component/view/View.h"
struct DummyEdge;
@@ -14,6 +15,7 @@ class Graph;
class GraphView
: public View
, public ScreenSearchResponder
{
public:
static const char* VIEW_NAME;
+24
View File
@@ -22,6 +22,7 @@ float GraphViewStyle::s_zoomFactor;
std::map<std::string, GraphViewStyle::NodeColor> GraphViewStyle::s_nodeColors;
std::map<std::string, std::string> GraphViewStyle::s_edgeColors;
std::map<bool, GraphViewStyle::NodeColor> GraphViewStyle::s_screenMatchColors;
Vec2i GraphViewStyle::alignOnRaster(Vec2i position)
{
@@ -142,6 +143,7 @@ void GraphViewStyle::loadStyleSettings()
s_nodeColors.clear();
s_edgeColors.clear();
s_screenMatchColors.clear();
s_gridCellPadding = getImpl()->getCharHeightForNodeType(Node::NODE_TYPE) - 8;
s_gridCellSize = s_gridCellPadding / 2;
@@ -713,6 +715,28 @@ const std::string& GraphViewStyle::getEdgeColor(const std::string& typeStr, bool
return s_edgeColors.find(type)->second;
}
const GraphViewStyle::NodeColor& GraphViewStyle::getScreenMatchColor(bool focus)
{
auto it = s_screenMatchColors.find(focus);
if (it != s_screenMatchColors.end())
{
return it->second;
}
NodeColor color;
ColorScheme* scheme = ColorScheme::getInstance().get();
ColorScheme::ColorState state = focus ? ColorScheme::FOCUS : ColorScheme::NORMAL;
color.fill = scheme->getCodeAnnotationTypeColor("screen_search", "fill", state);
color.border = scheme->getCodeAnnotationTypeColor("screen_search", "border", state);
color.text = scheme->getCodeAnnotationTypeColor("screen_search", "text", state);
s_screenMatchColors.emplace(focus, color);
return s_screenMatchColors.find(focus)->second;
}
void GraphViewStyle::addIcon(Node::NodeType type, bool hasChildren, NodeStyle* style)
{
switch (type)
+2
View File
@@ -138,6 +138,7 @@ public:
static const NodeColor& getNodeColor(const std::string& typeStr, bool focus);
static const std::string& getEdgeColor(const std::string& typeStr, bool focus);
static const NodeColor& getScreenMatchColor(bool focus);
static int s_gridCellSize;
static int s_gridCellPadding;
@@ -156,6 +157,7 @@ private:
static std::map<std::string, NodeColor> s_nodeColors;
static std::map<std::string, std::string> s_edgeColors;
static std::map<bool, NodeColor> s_screenMatchColors;
};
#endif // GRAPH_VIEW_STYLE_H
@@ -0,0 +1,15 @@
#include "component/view/ScreenSearchView.h"
ScreenSearchView::ScreenSearchView(ViewLayout* viewLayout)
: View(viewLayout)
{
}
ScreenSearchView::~ScreenSearchView()
{
}
std::string ScreenSearchView::getName() const
{
return "ScreenSearchView";
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef SCREEN_SEARCH_VIEW_H
#define SCREEN_SEARCH_VIEW_H
#include "component/view/View.h"
class ScreenSearchView
: public View
{
public:
ScreenSearchView(ViewLayout* viewLayout);
virtual ~ScreenSearchView();
// View implementation
virtual std::string getName() const;
virtual void setMatchCount(size_t matchCount) = 0;
virtual void setMatchIndex(size_t matchIndex) = 0;
virtual void addResponder(const std::string& name) = 0;
};
#endif // SCREEN_SEARCH_VIEW_H
+3
View File
@@ -8,6 +8,7 @@
#include "component/view/ViewLayout.h"
class ViewWidgetWrapper;
class ControllerProxy;
class View
{
@@ -43,6 +44,8 @@ protected:
void setWidgetWrapper(std::shared_ptr<ViewWidgetWrapper> widgetWrapper);
private:
friend ControllerProxy;
Component* m_component;
ViewLayout* const m_viewLayout;
std::shared_ptr<ViewWidgetWrapper> m_widgetWrapper;
+2
View File
@@ -13,6 +13,7 @@ class GraphView;
class MainView;
class LogView;
class RefreshView;
class ScreenSearchView;
class SearchView;
class StatusBarView;
class StatusView;
@@ -39,6 +40,7 @@ public:
virtual std::shared_ptr<GraphView> createGraphView(ViewLayout* viewLayout) const = 0;
virtual std::shared_ptr<LogView> createLogView(ViewLayout* viewLayout) const = 0;
virtual std::shared_ptr<RefreshView> createRefreshView(ViewLayout* viewLayout) const = 0;
virtual std::shared_ptr<ScreenSearchView> createScreenSearchView(ViewLayout* viewLayout) const = 0;
virtual std::shared_ptr<SearchView> createSearchView(ViewLayout* viewLayout) const = 0;
virtual std::shared_ptr<StatusBarView> createStatusBarView(ViewLayout* viewLayout) const = 0;
virtual std::shared_ptr<StatusView> createStatusView(ViewLayout* viewLayout) const = 0;
+8 -4
View File
@@ -10,10 +10,12 @@ int locationTypeToInt(LocationType type)
return 1;
case LOCATION_LOCAL_SYMBOL:
return 2;
case LOCATION_FULLTEXT:
return 3;
case LOCATION_ERROR:
return 3;
case LOCATION_FULLTEXT_SEARCH:
return 4;
case LOCATION_SCREEN_SEARCH:
return 5;
}
}
@@ -28,9 +30,11 @@ LocationType intToLocationType(int value)
case 2:
return LOCATION_LOCAL_SYMBOL;
case 3:
return LOCATION_FULLTEXT;
case 4:
return LOCATION_ERROR;
case 4:
return LOCATION_FULLTEXT_SEARCH;
case 5:
return LOCATION_SCREEN_SEARCH;
}
return LOCATION_TOKEN;
}
+3 -2
View File
@@ -6,8 +6,9 @@ enum LocationType
LOCATION_TOKEN,
LOCATION_SCOPE,
LOCATION_LOCAL_SYMBOL,
LOCATION_FULLTEXT,
LOCATION_ERROR
LOCATION_ERROR,
LOCATION_FULLTEXT_SEARCH,
LOCATION_SCREEN_SEARCH
};
int locationTypeToInt(LocationType type);
+1 -1
View File
@@ -177,7 +177,7 @@ bool SourceLocation::isScopeLocation() const
bool SourceLocation::isFullTextSearchMatch() const
{
return m_type == LOCATION_FULLTEXT;
return m_type == LOCATION_FULLTEXT_SEARCH;
}
std::ostream& operator<<(std::ostream& ostream, const SourceLocation& location)
+1 -1
View File
@@ -557,7 +557,7 @@ std::shared_ptr<SourceLocationCollection> PersistentStorage::getFullTextSearchLo
Id locationId = ~(~Id(0) >> 1) + collection->getSourceLocationCount() + 1;
collection->addSourceLocation(
LOCATION_FULLTEXT,
LOCATION_FULLTEXT_SEARCH,
locationId,
std::vector<Id>(),
filePath,