diff --git a/bin/app/data/color_schemes/bad_rainbow.xml b/bin/app/data/color_schemes/bad_rainbow.xml index aa837bc2..d6e97900 100644 --- a/bin/app/data/color_schemes/bad_rainbow.xml +++ b/bin/app/data/color_schemes/bad_rainbow.xml @@ -181,7 +181,7 @@ #FFFF0000 - + #C2AFEF #3D3D3D @@ -189,7 +189,17 @@ #987CEA - + + + + transparent + #D2FA6E + #262626 + + + #FA6EF3 + + @@ -436,6 +446,14 @@ + + + #494949 + #D4D4D4 + #878787 + #878787 + + #494949
#D4D4D4
@@ -452,10 +470,22 @@ #7B7B7B - - #494949 - #D4D4D4 - #878787 - #878787 - + + + #808080 + + #494949 + #D4D4D4 + #A0A0A0 + + white + + diff --git a/bin/app/data/color_schemes/bright.xml b/bin/app/data/color_schemes/bright.xml index e603b443..3adbfe72 100644 --- a/bin/app/data/color_schemes/bright.xml +++ b/bin/app/data/color_schemes/bright.xml @@ -179,7 +179,7 @@ #CCFF0000 - + #C7C7E2 #000000 @@ -187,7 +187,17 @@ #ABABDD - + + + + transparent + #8ADBBC + #000000 + + + #DB97C2 + + @@ -397,15 +407,14 @@ - - white - + white black #878787 #878787 + white
#aaa
@@ -422,4 +431,22 @@ grey + + + #E0E0E0 + + white + black + #A0A0A0 + + black + + diff --git a/bin/app/data/color_schemes/dark.xml b/bin/app/data/color_schemes/dark.xml index 27d1d63f..54faec61 100644 --- a/bin/app/data/color_schemes/dark.xml +++ b/bin/app/data/color_schemes/dark.xml @@ -181,7 +181,7 @@ #FFFF0000 - + #207EA8 #F7F7F7 @@ -190,7 +190,17 @@ #006D93 #134B63 - + + + + transparent + #6EDB42 + #000000 + + + #FF52E8 + + @@ -407,6 +417,14 @@ + + + #272728 + white + #878787 + #878787 + + #272728
white
@@ -423,10 +441,22 @@ #969696 - - #272728 - white - #878787 - #878787 - + + + #777777 + + #272728 + #F7F7F7 + #A0A0A0 + + #F7F7F7 + + diff --git a/bin/app/data/gui/screen_search_view/images/close.png b/bin/app/data/gui/screen_search_view/images/close.png new file mode 100755 index 00000000..e7ac9b40 Binary files /dev/null and b/bin/app/data/gui/screen_search_view/images/close.png differ diff --git a/bin/app/data/gui/screen_search_view/screen_search_view.css b/bin/app/data/gui/screen_search_view/screen_search_view.css new file mode 100644 index 00000000..80258f2c --- /dev/null +++ b/bin/app/data/gui/screen_search_view/screen_search_view.css @@ -0,0 +1,82 @@ +#screen_search_bar { + border: none; +} + +#screen_search_box { + padding-left: 10; + border-top: 1px solid #BEBEBE; + background: ; +} + +#search_box { + background: ; + border: none; + color: ; + font-family: ; + font-size: px; + margin: 4px 0; + max-width: px; + min-height: 20px; + padding: 1px 0px 1px 0px; +} + +#match_label { + border: none; + background: ; + color: ; + font-size: px; + margin: 4px 0; + padding-right: 4px; + text-align: right; + min-width: px; + min-height: 20px; + max-height: 40px; +} + +#search_button, #prev_button, #next_button, #close_button { + background: ; + width: 20px; + min-height: 20px; + max-height: 40px; + border-radius: 0; + margin: 4px 0; +} + +#search_button { + border-bottom-left-radius: 10px; + border-top-left-radius: 10px; + padding-left: 2px; +} + +#prev_button { + margin: 4px 1px; +} + +#next_button { + border-bottom-right-radius: 10px; + border-top-right-radius: 10px; +} + +#prev_button:disabled, #next_button:disabled { + background: ; +} + +#prev_button:hover, #next_button:hover { + background: ; +} + +#prev_button:pressed, #next_button:pressed { + background: ; +} + +#close_button { + background: transparent; + margin: 4px 6px; + padding-top: 1px; +} + +#filter_checkbox { + color: ; + margin-left: 20px; + font-size: px; +} diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 6745d73e..503ab534 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -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 diff --git a/src/lib/component/Component.cpp b/src/lib/component/Component.cpp index b5f580d8..38d1a415 100644 --- a/src/lib/component/Component.cpp +++ b/src/lib/component/Component.cpp @@ -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(); +} diff --git a/src/lib/component/Component.h b/src/lib/component/Component.h index c6d6c5fa..a7ad841a 100644 --- a/src/lib/component/Component.h +++ b/src/lib/component/Component.h @@ -15,9 +15,13 @@ public: template ControllerType* getController() const; + Controller* getControllerPtr() const; + template ViewType* getView() const; + View* getViewPtr() const; + private: const std::shared_ptr m_controller; const std::shared_ptr m_view; diff --git a/src/lib/component/ComponentFactory.cpp b/src/lib/component/ComponentFactory.cpp index 882ffa67..ceda2354 100644 --- a/src/lib/component/ComponentFactory.cpp +++ b/src/lib/component/ComponentFactory.cpp @@ -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 ComponentFactory::createActivationComponent() return std::make_shared(nullptr, controller); } -std::shared_ptr ComponentFactory::createTooltipComponent(ViewLayout* viewLayout) -{ - std::shared_ptr view = m_viewFactory->createTooltipView(viewLayout); - std::shared_ptr controller = std::make_shared(m_storageAccess); - - return std::make_shared(view, controller); -} - std::shared_ptr ComponentFactory::createBookmarkComponent(ViewLayout* viewLayout) { std::shared_ptr view = m_viewFactory->createBookmarkView(viewLayout); @@ -90,6 +84,14 @@ std::shared_ptr ComponentFactory::createErrorComponent(ViewLayout* vi return std::make_shared(view, controller); } +std::shared_ptr ComponentFactory::createGraphComponent(ViewLayout* viewLayout) +{ + std::shared_ptr view = m_viewFactory->createGraphView(viewLayout); + std::shared_ptr controller = std::make_shared(m_storageAccess); + + return std::make_shared(view, controller); +} + std::shared_ptr ComponentFactory::createLogComponent(ViewLayout* viewLayout) { std::shared_ptr view = m_viewFactory->createLogView(viewLayout); @@ -100,14 +102,6 @@ std::shared_ptr ComponentFactory::createLogComponent(ViewLayout* view return std::make_shared(view, controller); } -std::shared_ptr ComponentFactory::createGraphComponent(ViewLayout* viewLayout) -{ - std::shared_ptr view = m_viewFactory->createGraphView(viewLayout); - std::shared_ptr controller = std::make_shared(m_storageAccess); - - return std::make_shared(view, controller); -} - std::shared_ptr ComponentFactory::createRefreshComponent(ViewLayout* viewLayout) { std::shared_ptr view = m_viewFactory->createRefreshView(viewLayout); @@ -116,18 +110,18 @@ std::shared_ptr ComponentFactory::createRefreshComponent(ViewLayout* return std::make_shared(view, controller); } -std::shared_ptr ComponentFactory::createSearchComponent(ViewLayout* viewLayout) +std::shared_ptr ComponentFactory::createScreenSearchComponent(ViewLayout* viewLayout) { - std::shared_ptr view = m_viewFactory->createSearchView(viewLayout); - std::shared_ptr controller = std::make_shared(m_storageAccess); + std::shared_ptr view = m_viewFactory->createScreenSearchView(viewLayout); + std::shared_ptr controller = std::make_shared(); return std::make_shared(view, controller); } -std::shared_ptr ComponentFactory::createUndoRedoComponent(ViewLayout* viewLayout) +std::shared_ptr ComponentFactory::createSearchComponent(ViewLayout* viewLayout) { - std::shared_ptr view = m_viewFactory->createUndoRedoView(viewLayout); - std::shared_ptr controller = std::make_shared(m_storageAccess); + std::shared_ptr view = m_viewFactory->createSearchView(viewLayout); + std::shared_ptr controller = std::make_shared(m_storageAccess); return std::make_shared(view, controller); } @@ -148,6 +142,22 @@ std::shared_ptr ComponentFactory::createStatusComponent(ViewLayout* v return std::make_shared(view, controller); } +std::shared_ptr ComponentFactory::createTooltipComponent(ViewLayout* viewLayout) +{ + std::shared_ptr view = m_viewFactory->createTooltipView(viewLayout); + std::shared_ptr controller = std::make_shared(m_storageAccess); + + return std::make_shared(view, controller); +} + +std::shared_ptr ComponentFactory::createUndoRedoComponent(ViewLayout* viewLayout) +{ + std::shared_ptr view = m_viewFactory->createUndoRedoView(viewLayout); + std::shared_ptr controller = std::make_shared(m_storageAccess); + + return std::make_shared(view, controller); +} + ComponentFactory::ComponentFactory() { } diff --git a/src/lib/component/ComponentFactory.h b/src/lib/component/ComponentFactory.h index 433941b7..ccec633d 100644 --- a/src/lib/component/ComponentFactory.h +++ b/src/lib/component/ComponentFactory.h @@ -26,6 +26,7 @@ public: std::shared_ptr createGraphComponent(ViewLayout* viewLayout); std::shared_ptr createLogComponent(ViewLayout* viewLayout); std::shared_ptr createRefreshComponent(ViewLayout* viewLayout); + std::shared_ptr createScreenSearchComponent(ViewLayout* viewLayout); std::shared_ptr createSearchComponent(ViewLayout* viewLayout); std::shared_ptr createStatusBarComponent(ViewLayout* viewLayout); std::shared_ptr createStatusComponent(ViewLayout* viewLayout); diff --git a/src/lib/component/ComponentManager.cpp b/src/lib/component/ComponentManager.cpp index 62b35eaa..a3d4df65 100644 --- a/src/lib/component/ComponentManager.cpp +++ b/src/lib/component/ComponentManager.cpp @@ -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 tooltipComponent = m_componentFactory->createTooltipComponent(viewLayout); m_components.push_back(tooltipComponent); + std::shared_ptr screenSearchComponent = m_componentFactory->createScreenSearchComponent(viewLayout); + ScreenSearchController* screenSearchController = screenSearchComponent->getController(); + screenSearchController->addResponder(dynamic_cast(graphComponent->getViewPtr())); + screenSearchController->addResponder(dynamic_cast(codeComponent->getViewPtr())); + m_components.push_back(screenSearchComponent); + m_dialogView = m_componentFactory->getViewFactory()->createDialogView(viewLayout, m_componentFactory->getStorageAccess()); std::shared_ptr tabbedView = diff --git a/src/lib/component/controller/ScreenSearchController.cpp b/src/lib/component/controller/ScreenSearchController.cpp new file mode 100644 index 00000000..e107cd18 --- /dev/null +++ b/src/lib/component/controller/ScreenSearchController.cpp @@ -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 lock(m_matchMutex); + + size_t responderId = getResponderId(responder); + if (!responderId) + { + return; + } + + std::vector> 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()->setMatchCount(m_matches.size()); +} + +void ScreenSearchController::addResponder(ScreenSearchResponder* responder) +{ + if (responder) + { + m_responders.push_back(responder); + getView()->addResponder(responder->getName()); + } +} + +void ScreenSearchController::search(const std::string& query, const std::set& responderNames) +{ + { + std::lock_guard lock(m_matchMutex); + m_matches.clear(); + m_matchIndex = 0; + } + + getView()->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 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()->setMatchIndex(m_matchIndex + 1); +} + +void ScreenSearchController::clearMatches() +{ + { + std::lock_guard 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; +} diff --git a/src/lib/component/controller/ScreenSearchController.h b/src/lib/component/controller/ScreenSearchController.h new file mode 100644 index 00000000..f763e85c --- /dev/null +++ b/src/lib/component/controller/ScreenSearchController.h @@ -0,0 +1,40 @@ +#ifndef SCREEN_SEARCH_CONTROLLER_H +#define SCREEN_SEARCH_CONTROLLER_H + +#include +#include + +#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& responderNames); + void activateMatch(bool next); + void clearMatches(); + +private: + size_t getResponderId(ScreenSearchResponder* responder) const; + + std::vector m_responders; + + std::vector> m_matches; + size_t m_matchIndex = 0; + std::mutex m_matchMutex; +}; + +#endif // SCREEN_SEARCH_CONTROLLER_H diff --git a/src/lib/component/controller/helper/ControllerProxy.h b/src/lib/component/controller/helper/ControllerProxy.h new file mode 100644 index 00000000..bba5eb3f --- /dev/null +++ b/src/lib/component/controller/helper/ControllerProxy.h @@ -0,0 +1,47 @@ +#ifndef CONTROLLER_PROXY_H +#define CONTROLLER_PROXY_H + +#include + +#include "utility/scheduling/TaskLambda.h" + +#include "component/view/View.h" + +class ControllerProxy +{ +public: + ControllerProxy(View* view) + : m_view(view) + { + } + + template + void execute(std::function callback) + { + ControllerType* controller = m_view->getController(); + if (controller) + { + callback(controller); + } + } + + template + void executeAsTask(std::function callback) + { + ControllerType* controller = m_view->getController(); + if (controller) + { + Task::dispatch(std::make_shared( + [callback, controller]() + { + callback(controller); + } + )); + } + } + +private: + View* m_view; +}; + +#endif // CONTROLLER_PROXY_H diff --git a/src/lib/component/controller/helper/ScreenSearchInterfaces.h b/src/lib/component/controller/helper/ScreenSearchInterfaces.h new file mode 100644 index 00000000..3110fd95 --- /dev/null +++ b/src/lib/component/controller/helper/ScreenSearchInterfaces.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 diff --git a/src/lib/component/view/CodeView.h b/src/lib/component/view/CodeView.h index 74b497db..6e8f0d4c 100644 --- a/src/lib/component/view/CodeView.h +++ b/src/lib/component/view/CodeView.h @@ -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; diff --git a/src/lib/component/view/GraphView.h b/src/lib/component/view/GraphView.h index 2a5d1277..97f3fbef 100644 --- a/src/lib/component/view/GraphView.h +++ b/src/lib/component/view/GraphView.h @@ -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; diff --git a/src/lib/component/view/GraphViewStyle.cpp b/src/lib/component/view/GraphViewStyle.cpp index 33bd11ea..4ec1c636 100644 --- a/src/lib/component/view/GraphViewStyle.cpp +++ b/src/lib/component/view/GraphViewStyle.cpp @@ -22,6 +22,7 @@ float GraphViewStyle::s_zoomFactor; std::map GraphViewStyle::s_nodeColors; std::map GraphViewStyle::s_edgeColors; +std::map 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) diff --git a/src/lib/component/view/GraphViewStyle.h b/src/lib/component/view/GraphViewStyle.h index e86e4108..a22a08dd 100644 --- a/src/lib/component/view/GraphViewStyle.h +++ b/src/lib/component/view/GraphViewStyle.h @@ -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 s_nodeColors; static std::map s_edgeColors; + static std::map s_screenMatchColors; }; #endif // GRAPH_VIEW_STYLE_H diff --git a/src/lib/component/view/ScreenSearchView.cpp b/src/lib/component/view/ScreenSearchView.cpp new file mode 100644 index 00000000..2157b03d --- /dev/null +++ b/src/lib/component/view/ScreenSearchView.cpp @@ -0,0 +1,15 @@ +#include "component/view/ScreenSearchView.h" + +ScreenSearchView::ScreenSearchView(ViewLayout* viewLayout) + : View(viewLayout) +{ +} + +ScreenSearchView::~ScreenSearchView() +{ +} + +std::string ScreenSearchView::getName() const +{ + return "ScreenSearchView"; +} diff --git a/src/lib/component/view/ScreenSearchView.h b/src/lib/component/view/ScreenSearchView.h new file mode 100644 index 00000000..7fd57f1c --- /dev/null +++ b/src/lib/component/view/ScreenSearchView.h @@ -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 diff --git a/src/lib/component/view/View.h b/src/lib/component/view/View.h index 77c9b115..1712b864 100644 --- a/src/lib/component/view/View.h +++ b/src/lib/component/view/View.h @@ -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 widgetWrapper); private: + friend ControllerProxy; + Component* m_component; ViewLayout* const m_viewLayout; std::shared_ptr m_widgetWrapper; diff --git a/src/lib/component/view/ViewFactory.h b/src/lib/component/view/ViewFactory.h index aba4675a..18749da1 100644 --- a/src/lib/component/view/ViewFactory.h +++ b/src/lib/component/view/ViewFactory.h @@ -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 createGraphView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createLogView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createRefreshView(ViewLayout* viewLayout) const = 0; + virtual std::shared_ptr createScreenSearchView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createSearchView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createStatusBarView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createStatusView(ViewLayout* viewLayout) const = 0; diff --git a/src/lib/data/location/LocationType.cpp b/src/lib/data/location/LocationType.cpp index ea932f05..eb3b2b31 100644 --- a/src/lib/data/location/LocationType.cpp +++ b/src/lib/data/location/LocationType.cpp @@ -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; } diff --git a/src/lib/data/location/LocationType.h b/src/lib/data/location/LocationType.h index 52ca50be..2dc173db 100644 --- a/src/lib/data/location/LocationType.h +++ b/src/lib/data/location/LocationType.h @@ -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); diff --git a/src/lib/data/location/SourceLocation.cpp b/src/lib/data/location/SourceLocation.cpp index 7b7521c0..3bea1974 100644 --- a/src/lib/data/location/SourceLocation.cpp +++ b/src/lib/data/location/SourceLocation.cpp @@ -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) diff --git a/src/lib/data/storage/PersistentStorage.cpp b/src/lib/data/storage/PersistentStorage.cpp index 8e87e610..a533311e 100644 --- a/src/lib/data/storage/PersistentStorage.cpp +++ b/src/lib/data/storage/PersistentStorage.cpp @@ -557,7 +557,7 @@ std::shared_ptr PersistentStorage::getFullTextSearchLo Id locationId = ~(~Id(0) >> 1) + collection->getSourceLocationCount() + 1; collection->addSourceLocation( - LOCATION_FULLTEXT, + LOCATION_FULLTEXT_SEARCH, locationId, std::vector(), filePath, diff --git a/src/lib_gui/CMakeLists.txt b/src/lib_gui/CMakeLists.txt index 9d94fe1e..18734fad 100644 --- a/src/lib_gui/CMakeLists.txt +++ b/src/lib_gui/CMakeLists.txt @@ -48,6 +48,8 @@ add_files( qt/element/QtProgressBar.h qt/element/QtRefreshBar.cpp qt/element/QtRefreshBar.h + qt/element/QtScreenSearchBox.cpp + qt/element/QtScreenSearchBox.h qt/element/QtSearchBar.cpp qt/element/QtSearchBar.h qt/element/QtSmartSearchBox.cpp @@ -145,6 +147,8 @@ add_files( qt/view/QtMainView.h qt/view/QtRefreshView.cpp qt/view/QtRefreshView.h + qt/view/QtScreenSearchView.cpp + qt/view/QtScreenSearchView.h qt/view/QtSearchView.cpp qt/view/QtSearchView.h qt/view/QtStatusBarView.cpp diff --git a/src/lib_gui/qt/element/QtCodeArea.cpp b/src/lib_gui/qt/element/QtCodeArea.cpp index 42b12a06..35f25593 100644 --- a/src/lib_gui/qt/element/QtCodeArea.cpp +++ b/src/lib_gui/qt/element/QtCodeArea.cpp @@ -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>* 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 start = toLineColumn(matchAnnotation.start); + matchAnnotation.startLine = start.first; + matchAnnotation.startCol = start.second; + + std::pair 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); diff --git a/src/lib_gui/qt/element/QtCodeArea.h b/src/lib_gui/qt/element/QtCodeArea.h index 0185066c..25d331e0 100644 --- a/src/lib_gui/qt/element/QtCodeArea.h +++ b/src/lib_gui/qt/element/QtCodeArea.h @@ -83,6 +83,9 @@ public: QRectF getLineRectForLineNumber(uint lineNumber) const; + void findScreenMatches(const std::string& query, std::vector>* screenMatches); + void clearScreenMatches(); + protected: virtual void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE; virtual void mouseReleaseEvent(QMouseEvent* event) Q_DECL_OVERRIDE; diff --git a/src/lib_gui/qt/element/QtCodeField.cpp b/src/lib_gui/qt/element/QtCodeField.cpp index 2506a9d7..c7467c12 100644 --- a/src/lib_gui/qt/element/QtCodeField.cpp +++ b/src/lib_gui/qt/element/QtCodeField.cpp @@ -549,7 +549,7 @@ const QtCodeField::AnnotationColor& QtCodeField::getAnnotationColorForAnnotation if (!s_annotationColors.size()) { ColorScheme* scheme = ColorScheme::getInstance().get(); - std::vector types = { "token", "local_symbol", "scope", "error", "fulltext" }; + std::vector types = { "token", "local_symbol", "scope", "error", "fulltext_search", "screen_search" }; std::vector 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) { diff --git a/src/lib_gui/qt/element/QtCodeField.h b/src/lib_gui/qt/element/QtCodeField.h index a33f7565..b7ed38b8 100644 --- a/src/lib_gui/qt/element/QtCodeField.h +++ b/src/lib_gui/qt/element/QtCodeField.h @@ -99,6 +99,7 @@ protected: std::vector m_annotations; std::vector m_hoveredAnnotations; + std::vector m_linesToRehighlight; private: static std::vector s_annotationColors; @@ -113,7 +114,6 @@ private: QtHighlighter* m_highlighter; std::vector m_lineLengths; - std::vector m_linesToRehighlight; int m_endTextEditPosition; }; diff --git a/src/lib_gui/qt/element/QtCodeFile.cpp b/src/lib_gui/qt/element/QtCodeFile.cpp index 2b74e1b0..fbe16a95 100644 --- a/src/lib_gui/qt/element/QtCodeFile.cpp +++ b/src/lib_gui/qt/element/QtCodeFile.cpp @@ -143,13 +143,12 @@ QtCodeSnippet* QtCodeFile::addCodeSnippet(const CodeSnippetParams& params) } } - + m_isCollapsed = false; std::shared_ptr 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& 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>* screenMatches) +{ + if (m_fileSnippet && m_fileSnippet->isVisible()) + { + m_fileSnippet->findScreenMatches(query, screenMatches); + return; + } + + for (const std::shared_ptr& snippet : m_snippets) + { + if (snippet->isVisible()) + { + snippet->findScreenMatches(query, screenMatches); + } + } +} + void QtCodeFile::clickedMinimizeButton() { // overview stats diff --git a/src/lib_gui/qt/element/QtCodeFile.h b/src/lib_gui/qt/element/QtCodeFile.h index 9a39ddbc..d5f6716c 100644 --- a/src/lib_gui/qt/element/QtCodeFile.h +++ b/src/lib_gui/qt/element/QtCodeFile.h @@ -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>* screenMatches); + public slots: void clickedMinimizeButton(); void clickedSnippetButton(); diff --git a/src/lib_gui/qt/element/QtCodeFileList.cpp b/src/lib_gui/qt/element/QtCodeFileList.cpp index b6f457af..da911cd6 100644 --- a/src/lib_gui/qt/element/QtCodeFileList.cpp +++ b/src/lib_gui/qt/element/QtCodeFileList.cpp @@ -199,6 +199,14 @@ void QtCodeFileList::onWindowFocus() } } +void QtCodeFileList::findScreenMatches(const std::string& query, std::vector>* screenMatches) +{ + for (const std::shared_ptr& filePtr : m_files) + { + filePtr->findScreenMatches(query, screenMatches); + } +} + void QtCodeFileList::setFileMinimized(const FilePath path) { getFile(path)->setMinimized(); diff --git a/src/lib_gui/qt/element/QtCodeFileList.h b/src/lib_gui/qt/element/QtCodeFileList.h index 6d6d020c..97afc43e 100644 --- a/src/lib_gui/qt/element/QtCodeFileList.h +++ b/src/lib_gui/qt/element/QtCodeFileList.h @@ -43,6 +43,8 @@ public: virtual void onWindowFocus(); + virtual void findScreenMatches(const std::string& query, std::vector>* screenMatches); + void setFileMinimized(const FilePath path); void setFileSnippets(const FilePath path); void setFileMaximized(const FilePath path); diff --git a/src/lib_gui/qt/element/QtCodeFileSingle.cpp b/src/lib_gui/qt/element/QtCodeFileSingle.cpp index d70936e8..10071ec8 100644 --- a/src/lib_gui/qt/element/QtCodeFileSingle.cpp +++ b/src/lib_gui/qt/element/QtCodeFileSingle.cpp @@ -225,6 +225,14 @@ void QtCodeFileSingle::onWindowFocus() m_title->updateTexts(); } +void QtCodeFileSingle::findScreenMatches(const std::string& query, std::vector>* screenMatches) +{ + if (m_area) + { + m_area->findScreenMatches(query, screenMatches); + } +} + const FilePath& QtCodeFileSingle::getCurrentFilePath() const { return m_currentFilePath; diff --git a/src/lib_gui/qt/element/QtCodeFileSingle.h b/src/lib_gui/qt/element/QtCodeFileSingle.h index 456905dd..3bb18edc 100644 --- a/src/lib_gui/qt/element/QtCodeFileSingle.h +++ b/src/lib_gui/qt/element/QtCodeFileSingle.h @@ -43,6 +43,8 @@ public: virtual void onWindowFocus() override; + virtual void findScreenMatches(const std::string& query, std::vector>* screenMatches) override; + const FilePath& getCurrentFilePath() const; bool hasFileCached(const FilePath& filePath) const; diff --git a/src/lib_gui/qt/element/QtCodeNavigateable.h b/src/lib_gui/qt/element/QtCodeNavigateable.h index ba4931a4..8bf09aef 100644 --- a/src/lib_gui/qt/element/QtCodeNavigateable.h +++ b/src/lib_gui/qt/element/QtCodeNavigateable.h @@ -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>* 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); diff --git a/src/lib_gui/qt/element/QtCodeNavigator.cpp b/src/lib_gui/qt/element/QtCodeNavigator.cpp index e0bc412c..96c4630e 100644 --- a/src/lib_gui/qt/element/QtCodeNavigator.cpp +++ b/src/lib_gui/qt/element/QtCodeNavigator.cpp @@ -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 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) diff --git a/src/lib_gui/qt/element/QtCodeNavigator.h b/src/lib_gui/qt/element/QtCodeNavigator.h index 4e4423f0..4dfc2f62 100644 --- a/src/lib_gui/qt/element/QtCodeNavigator.h +++ b/src/lib_gui/qt/element/QtCodeNavigator.h @@ -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> m_screenMatches; + Id m_activeScreenMatchId = 0; }; #endif // QT_CODE_NAVIGATOR_H diff --git a/src/lib_gui/qt/element/QtCodeSnippet.cpp b/src/lib_gui/qt/element/QtCodeSnippet.cpp index a4ffda37..ecddfde0 100644 --- a/src/lib_gui/qt/element/QtCodeSnippet.cpp +++ b/src/lib_gui/qt/element/QtCodeSnippet.cpp @@ -187,6 +187,11 @@ std::string QtCodeSnippet::getCode() const return m_codeArea->getCode(); } +void QtCodeSnippet::findScreenMatches(const std::string& query, std::vector>* screenMatches) +{ + m_codeArea->findScreenMatches(query, screenMatches); +} + void QtCodeSnippet::clickedTitle() { if (m_titleId > 0) diff --git a/src/lib_gui/qt/element/QtCodeSnippet.h b/src/lib_gui/qt/element/QtCodeSnippet.h index ed43e80f..9040a57b 100644 --- a/src/lib_gui/qt/element/QtCodeSnippet.h +++ b/src/lib_gui/qt/element/QtCodeSnippet.h @@ -51,6 +51,8 @@ public: std::string getCode() const; + void findScreenMatches(const std::string& query, std::vector>* screenMatches); + private slots: void clickedTitle(); void clickedFooter(); diff --git a/src/lib_gui/qt/element/QtScreenSearchBox.cpp b/src/lib_gui/qt/element/QtScreenSearchBox.cpp new file mode 100644 index 00000000..7f43338d --- /dev/null +++ b/src/lib_gui/qt/element/QtScreenSearchBox.cpp @@ -0,0 +1,263 @@ +#include "qt/element/QtScreenSearchBox.h" + +#include +#include +#include +#include +#include +#include +#include + +#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(obj); + if (lineEdit && event->type() == QEvent::FocusIn && dynamic_cast(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( + [this](ScreenSearchController* controller) + { + std::set 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( + [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); +} diff --git a/src/lib_gui/qt/element/QtScreenSearchBox.h b/src/lib_gui/qt/element/QtScreenSearchBox.h new file mode 100644 index 00000000..ef9a4c83 --- /dev/null +++ b/src/lib_gui/qt/element/QtScreenSearchBox.h @@ -0,0 +1,84 @@ +#ifndef QT_SCREEN_SEARCH_BOX_H +#define QT_SCREEN_SEARCH_BOX_H + +#include + +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 m_checkBoxes; + + size_t m_matchCount = 0; + size_t m_matchIndex = 0; + + QTimer* m_timer; +}; + +#endif // QT_SCREEN_SEARCH_BOX_H diff --git a/src/lib_gui/qt/utility/QtHighlighter.cpp b/src/lib_gui/qt/utility/QtHighlighter.cpp index e6a171e4..af8bc536 100644 --- a/src/lib_gui/qt/utility/QtHighlighter.cpp +++ b/src/lib_gui/qt/utility/QtHighlighter.cpp @@ -5,6 +5,7 @@ #include #include "settings/ColorScheme.h" +#include "utility/utility.h" QVector QtHighlighter::s_highlightingRules; QVector QtHighlighter::s_highlightingRulesCpp; @@ -110,16 +111,16 @@ QtHighlighter::QtHighlighter(QTextDocument *document, LanguageType language) : m_document(document) , m_language(language) { - if (m_language == LANGUAGE_UNKNOWN) - { - return; - } - if (!s_highlightingRules.size()) { createHighlightingRules(); } + if (m_language == LANGUAGE_UNKNOWN) + { + return; + } + if (m_language == LANGUAGE_JAVA) { m_highlightingRules = s_highlightingRulesJava; @@ -134,11 +135,6 @@ QtHighlighter::QtHighlighter(QTextDocument *document, LanguageType language) void QtHighlighter::highlightDocument() { - if (m_language == LANGUAGE_UNKNOWN) - { - return; - } - QTextDocument* doc = document(); int docStart = 0; @@ -150,26 +146,29 @@ void QtHighlighter::highlightDocument() docEnd -= 1; applyFormat(docStart, docEnd, s_textFormat); - m_ranges.clear(); - m_highlightedLines.clear(); m_highlightedLines.resize(document()->blockCount(), false); - for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) - { - formatBlock(it, s_quotationRule, &m_ranges, true); - } - - highlightMultiLineComments(&m_ranges); -} - -void QtHighlighter::highlightRange(int startLine, int endLine) -{ if (m_language == LANGUAGE_UNKNOWN) { return; } + m_quotationRanges.clear(); + m_multiLineCommentRanges.clear(); + + std::vector> ranges; + ranges.push_back(std::pair(docStart, docEnd)); + for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) + { + utility::append(m_quotationRanges, formatBlockForRule(it, s_quotationRule, &ranges)); + } + + highlightMultiLineComments(); +} + +void QtHighlighter::highlightRange(int startLine, int endLine) +{ if (startLine < 0 || endLine < 0 || startLine > endLine || endLine > int(m_highlightedLines.size())) { return; @@ -199,22 +198,19 @@ void QtHighlighter::highlightRange(int startLine, int endLine) { if (!m_highlightedLines[index]) { - applyFormatWithoutRanges(it.position(), it.position() + it.length() - 1, s_textFormat, m_ranges); + applyFormat(it.position(), it.position() + it.length() - 1, s_textFormat); - foreach (const HighlightingRule &rule, m_highlightingRules) + if (m_language != LANGUAGE_UNKNOWN) { - formatBlock(it, rule, &m_ranges, false); - } - } - index++; - } + foreach (const HighlightingRule &rule, m_highlightingRules) + { + formatBlockForRule(it, rule); + } - index = startLine; - for (QTextBlock it = start; it != end; it = it.next()) - { - if (!m_highlightedLines[index]) - { - formatBlock(it, s_commentRule, &m_ranges, false); + formatBlockIfInRange(it, s_quotationRule.format, &m_quotationRanges); + formatBlockForRule(it, s_commentRule, &m_quotationRanges); + formatBlockIfInRange(it, s_commentRule.format, &m_multiLineCommentRanges); + } } index++; } @@ -244,40 +240,6 @@ void QtHighlighter::applyFormat(int startPosition, int endPosition, const QTextC cursor.setCharFormat(format); } -void QtHighlighter::applyFormatWithoutRanges( - int startPosition, int endPosition, const QTextCharFormat& format, const std::vector>& ranges -){ - std::vector> highlightRanges; - - for (const std::pair p : ranges) - { - if (p.first <= startPosition && p.second >= endPosition) - { - return; - } - - if (p.first > startPosition && p.first < endPosition) - { - highlightRanges.push_back(std::pair(startPosition, p.first - 1)); - } - - if (p.second > startPosition && p.second < endPosition) - { - highlightRanges.push_back(std::pair(p.second + 1, endPosition)); - } - } - - if (!highlightRanges.size()) - { - highlightRanges.push_back(std::pair(startPosition, endPosition)); - } - - for (const std::pair p : highlightRanges) - { - applyFormat(p.first, p.second, format); - } -} - QTextCharFormat QtHighlighter::getFormat(int startPosition, int endPosition) const { QTextCursor cursor(document()); @@ -285,7 +247,7 @@ QTextCharFormat QtHighlighter::getFormat(int startPosition, int endPosition) con return cursor.charFormat(); } -void QtHighlighter::highlightMultiLineComments(std::vector>* ranges) +void QtHighlighter::highlightMultiLineComments() { QTextDocument* doc = document(); @@ -305,7 +267,7 @@ void QtHighlighter::highlightMultiLineComments(std::vector>* cursorStart.setPosition(cursorStart.selectionEnd() - 2); } } - while (isInRange(cursorStart.position(), *ranges)); + while (isInRange(cursorStart.position(), m_quotationRanges)); if (cursorStart.isNull()) { @@ -318,9 +280,7 @@ void QtHighlighter::highlightMultiLineComments(std::vector>* break; } - applyFormat(cursorStart.selectionStart(), cursorEnd.position(), s_commentRule.format); - ranges->push_back(std::pair(cursorStart.selectionStart(), cursorEnd.position())); - + m_multiLineCommentRanges.push_back(std::pair(cursorStart.selectionStart(), cursorEnd.position())); cursorStart = cursorEnd; } } @@ -348,34 +308,47 @@ bool QtHighlighter::isInRange(int pos, const std::vector>& r return false; } -void QtHighlighter::formatBlock( - const QTextBlock& block, const HighlightingRule& rule, std::vector>* ranges, bool saveRange +std::vector> QtHighlighter::formatBlockForRule( + const QTextBlock& block, const HighlightingRule& rule, std::vector>* ranges ){ QRegExp expression(rule.pattern); int pos = block.position(); int index = expression.indexIn(block.text()); + std::vector> newRanges; while (index >= 0) { int length = expression.matchedLength(); - if (!isInRange(pos + index, *ranges)) + if (!ranges || !isInRange(pos + index, *ranges)) { applyFormat(pos + index, pos + index + length, rule.format); } - if (saveRange) - { - newRanges.push_back(std::pair(pos + index, pos + index + length - 1)); - } + newRanges.push_back(std::pair(pos + index, pos + index + length)); index = expression.indexIn(block.text(), index + length); } - if (saveRange) + return newRanges; +} + +void QtHighlighter::formatBlockIfInRange( + const QTextBlock& block, const QTextCharFormat& format, std::vector>* ranges +){ + int startPos = block.position(); + int endPos = startPos + block.length() - 1; + + for (auto range : *ranges) { - ranges->insert(ranges->end(), newRanges.begin(), newRanges.end()); + int start = std::max(range.first, startPos); + int end = std::min(range.second, endPos); + + if (start <= end) + { + applyFormat(start, end, format); + } } } diff --git a/src/lib_gui/qt/utility/QtHighlighter.h b/src/lib_gui/qt/utility/QtHighlighter.h index 989f8fbb..77e36f93 100644 --- a/src/lib_gui/qt/utility/QtHighlighter.h +++ b/src/lib_gui/qt/utility/QtHighlighter.h @@ -21,8 +21,6 @@ public: void rehighlightLines(const std::vector& lines); void applyFormat(int startPosition, int endPosition, const QTextCharFormat& format); - void applyFormatWithoutRanges( - int startPosition, int endPosition, const QTextCharFormat& format, const std::vector>& ranges); QTextCharFormat getFormat(int startPosition, int endPosition) const; @@ -36,11 +34,13 @@ private: QTextCharFormat format; }; - void highlightMultiLineComments(std::vector>* ranges); + void highlightMultiLineComments(); bool isInRange(int index, const std::vector>& ranges) const; - void formatBlock( - const QTextBlock& block, const HighlightingRule& rule, std::vector>* ranges, bool saveRange); + std::vector> formatBlockForRule( + const QTextBlock& block, const HighlightingRule& rule, std::vector>* ranges = nullptr); + void formatBlockIfInRange( + const QTextBlock& block, const QTextCharFormat& format, std::vector>* ranges); QTextDocument* document() const; @@ -56,7 +56,8 @@ private: LanguageType m_language; QVector m_highlightingRules; - std::vector> m_ranges; + std::vector> m_quotationRanges; + std::vector> m_multiLineCommentRanges; std::vector m_highlightedLines; }; diff --git a/src/lib_gui/qt/view/QtCodeView.cpp b/src/lib_gui/qt/view/QtCodeView.cpp index a65c2ff5..e6aa257f 100644 --- a/src/lib_gui/qt/view/QtCodeView.cpp +++ b/src/lib_gui/qt/view/QtCodeView.cpp @@ -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([=]() diff --git a/src/lib_gui/qt/view/QtCodeView.h b/src/lib_gui/qt/view/QtCodeView.h index 359f86d8..893ab41f 100644 --- a/src/lib_gui/qt/view/QtCodeView.h +++ b/src/lib_gui/qt/view/QtCodeView.h @@ -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(); diff --git a/src/lib_gui/qt/view/QtGraphView.cpp b/src/lib_gui/qt/view/QtGraphView.cpp index d128d0cd..9b2db149 100644 --- a/src/lib_gui/qt/view/QtGraphView.cpp +++ b/src/lib_gui/qt/view/QtGraphView.cpp @@ -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 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, const std::vector>& 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() diff --git a/src/lib_gui/qt/view/QtGraphView.h b/src/lib_gui/qt/view/QtGraphView.h index 778ec4c4..f13c3264 100644 --- a/src/lib_gui/qt/view/QtGraphView.h +++ b/src/lib_gui/qt/view/QtGraphView.h @@ -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, const std::vector>& nodes, @@ -164,6 +172,9 @@ private: QLabel* m_trailDepthLabel; std::vector m_virtualNodeRects; + + // Name matches + std::vector m_matchedNodes; }; #endif // QT_GRAPH_VIEW_H diff --git a/src/lib_gui/qt/view/QtScreenSearchView.cpp b/src/lib_gui/qt/view/QtScreenSearchView.cpp new file mode 100644 index 00000000..049dfec8 --- /dev/null +++ b/src/lib_gui/qt/view/QtScreenSearchView.cpp @@ -0,0 +1,98 @@ +#include "qt/view/QtScreenSearchView.h" + +#include + +#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(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(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(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* controller) + { + controller->clearMatches(); + } + ); +} diff --git a/src/lib_gui/qt/view/QtScreenSearchView.h b/src/lib_gui/qt/view/QtScreenSearchView.h new file mode 100644 index 00000000..8bd52356 --- /dev/null +++ b/src/lib_gui/qt/view/QtScreenSearchView.h @@ -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 diff --git a/src/lib_gui/qt/view/QtTooltipView.cpp b/src/lib_gui/qt/view/QtTooltipView.cpp index e0c9509e..82e0cbc8 100644 --- a/src/lib_gui/qt/view/QtTooltipView.cpp +++ b/src/lib_gui/qt/view/QtTooltipView.cpp @@ -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(getViewLayout())->getMainWindow()); + m_widget = new QtTooltip(dynamic_cast(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() - ); -} diff --git a/src/lib_gui/qt/view/QtTooltipView.h b/src/lib_gui/qt/view/QtTooltipView.h index a324ed70..6d5ea566 100644 --- a/src/lib_gui/qt/view/QtTooltipView.h +++ b/src/lib_gui/qt/view/QtTooltipView.h @@ -25,9 +25,6 @@ public: virtual bool tooltipVisible() const; private: - void setStyleSheet(); - void doRefreshView(); - QtThreadedLambdaFunctor m_onQtThread; QtTooltip* m_widget; diff --git a/src/lib_gui/qt/view/QtViewFactory.cpp b/src/lib_gui/qt/view/QtViewFactory.cpp index 958a70aa..dce70986 100644 --- a/src/lib_gui/qt/view/QtViewFactory.cpp +++ b/src/lib_gui/qt/view/QtViewFactory.cpp @@ -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 QtViewFactory::createRefreshView(ViewLayout* viewLa return View::createInitAndAddToLayout(viewLayout); } +std::shared_ptr QtViewFactory::createScreenSearchView(ViewLayout* viewLayout) const +{ + return View::createAndInit(viewLayout); +} + std::shared_ptr QtViewFactory::createSearchView(ViewLayout* viewLayout) const { return View::createInitAndAddToLayout(viewLayout); diff --git a/src/lib_gui/qt/view/QtViewFactory.h b/src/lib_gui/qt/view/QtViewFactory.h index d1f13777..ffc59736 100644 --- a/src/lib_gui/qt/view/QtViewFactory.h +++ b/src/lib_gui/qt/view/QtViewFactory.h @@ -20,6 +20,7 @@ public: virtual std::shared_ptr createGraphView(ViewLayout* viewLayout) const; virtual std::shared_ptr createLogView(ViewLayout* viewLayout) const; virtual std::shared_ptr createRefreshView(ViewLayout* viewLayout) const; + virtual std::shared_ptr createScreenSearchView(ViewLayout* viewLayout) const; virtual std::shared_ptr createSearchView(ViewLayout* viewLayout) const; virtual std::shared_ptr createStatusBarView(ViewLayout* viewLayout) const; virtual std::shared_ptr createStatusView(ViewLayout* viewLayout) const; diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp b/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp index fe9c8773..826abd25 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp +++ b/src/lib_gui/qt/view/graphElements/QtGraphNode.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -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* 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* 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); + } } diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNode.h b/src/lib_gui/qt/view/graphElements/QtGraphNode.h index 3a7a535a..046af9d5 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNode.h +++ b/src/lib_gui/qt/view/graphElements/QtGraphNode.h @@ -74,6 +74,10 @@ public: void focusIn(); void focusOut(); + void matchNameRecursive(const std::string& query, std::vector* 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* matchedNodes); + void setStyle(const GraphViewStyle::NodeStyle& style); std::list> m_outEdges; @@ -107,19 +113,26 @@ protected: std::weak_ptr m_parentNode; std::list> 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> 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 diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeAccess.h b/src/lib_gui/qt/view/graphElements/QtGraphNodeAccess.h index 9893f15e..9babdc7f 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNodeAccess.h +++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeAccess.h @@ -23,6 +23,9 @@ public: void hideLabel(); +protected: + virtual void matchName(const std::string& query, std::vector* matchedNodes) {} + private: AccessKind m_accessKind; diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeExpandToggle.h b/src/lib_gui/qt/view/graphElements/QtGraphNodeExpandToggle.h index d8162c01..8927348d 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNodeExpandToggle.h +++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeExpandToggle.h @@ -19,6 +19,9 @@ public: virtual void onClick(); virtual void updateStyle(); +protected: + virtual void matchName(const std::string& query, std::vector* matchedNodes) {} + private: QGraphicsPixmapItem* m_icon; bool m_invisibleSubNodeCount; diff --git a/src/lib_gui/qt/view/graphElements/QtGraphNodeQualifier.h b/src/lib_gui/qt/view/graphElements/QtGraphNodeQualifier.h index d53b2ad0..c5197319 100644 --- a/src/lib_gui/qt/view/graphElements/QtGraphNodeQualifier.h +++ b/src/lib_gui/qt/view/graphElements/QtGraphNodeQualifier.h @@ -26,6 +26,8 @@ protected: virtual void hoverEnterEvent(QGraphicsSceneHoverEvent* event); virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event); + virtual void matchName(const std::string& query, std::vector* matchedNodes) {} + private: const NameHierarchy m_qualifierName; diff --git a/src/lib_gui/qt/window/QtMainWindow.cpp b/src/lib_gui/qt/window/QtMainWindow.cpp index 75b75f14..58d9bef6 100644 --- a/src/lib_gui/qt/window/QtMainWindow.cpp +++ b/src/lib_gui/qt/window/QtMainWindow.cpp @@ -357,6 +357,7 @@ void QtMainWindow::keyPressEvent(QKeyEvent* event) case Qt::Key_Escape: MessageInterruptTasks().dispatch(); + emit hideScreenSearch(); break; case Qt::Key_Space: @@ -565,6 +566,11 @@ void QtMainWindow::findFulltext() MessageFind(true).dispatch(); } +void QtMainWindow::findInView() +{ + emit showScreenSearch(); +} + void QtMainWindow::codeReferencePrevious() { MessageCodeReference(MessageCodeReference::REFERENCE_PREVIOUS).dispatch(); @@ -771,6 +777,7 @@ void QtMainWindow::setupEditMenu() menu->addAction(tr("&Find Symbol"), this, &QtMainWindow::find, QKeySequence::Find); menu->addAction(tr("&Find Text"), this, &QtMainWindow::findFulltext, QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_F)); + menu->addAction(tr("&Find in View"), this, &QtMainWindow::findInView, QKeySequence(Qt::CTRL + Qt::Key_D)); menu->addSeparator(); @@ -858,7 +865,7 @@ void QtMainWindow::setupBookmarksMenu() m_bookmarksMenu->clear(); } - m_bookmarksMenu->addAction(tr("Bookmark Active Symbol..."), this, &QtMainWindow::showBookmarkCreator, QKeySequence(Qt::CTRL + Qt::Key_D)); + m_bookmarksMenu->addAction(tr("Bookmark Active Symbol..."), this, &QtMainWindow::showBookmarkCreator, QKeySequence::Save); m_bookmarksMenu->addAction(tr("Bookmark Manager"), this, &QtMainWindow::showBookmarkBrowser, QKeySequence(Qt::CTRL + Qt::Key_B)); m_bookmarksMenu->addSeparator(); diff --git a/src/lib_gui/qt/window/QtMainWindow.h b/src/lib_gui/qt/window/QtMainWindow.h index ecefb757..456f3f19 100644 --- a/src/lib_gui/qt/window/QtMainWindow.h +++ b/src/lib_gui/qt/window/QtMainWindow.h @@ -78,6 +78,10 @@ public: void setContentEnabled(bool enabled); +signals: + void showScreenSearch(); + void hideScreenSearch(); + protected: void keyPressEvent(QKeyEvent* event); void contextMenuEvent(QContextMenuEvent* event); @@ -109,6 +113,7 @@ public slots: void find(); void findFulltext(); + void findInView(); void codeReferencePrevious(); void codeReferenceNext(); void overview();