ui: Added custom trail dialog (issue #249)

* open custom trail dialg via button in top of trail controls
* search from symbol to symbol or all referenced/referencing of symbol
* define search depth with slider
* define horizontal/vertical layout
* define node and edge types
* renamed depth graph to trail in graph ui
* added custom trail UI tests

fortune cookie message = A happy surprise is waiting for you.
This commit is contained in:
Eberhard Graether
2019-07-29 11:25:34 +02:00
parent 6a2c59f3e5
commit 68c9782e33
54 changed files with 1740 additions and 130 deletions
+2
View File
@@ -246,6 +246,8 @@ add_files(
qt/view/QtCodeView.h
qt/view/QtCompositeView.cpp
qt/view/QtCompositeView.h
qt/view/QtCustomTrailView.cpp
qt/view/QtCustomTrailView.h
qt/view/QtDialogView.cpp
qt/view/QtDialogView.h
qt/view/QtErrorView.cpp
+25 -6
View File
@@ -3,10 +3,12 @@
#include <QHBoxLayout>
#include "MessageActivateAll.h"
#include "ResourcePaths.h"
#include "MessageActivateFullTextSearch.h"
#include "MessageSearch.h"
#include "MessageSearchAutocomplete.h"
#include "QtSearchBarButton.h"
#include "QtSmartSearchBox.h"
#include "ResourcePaths.h"
QtSearchBar::QtSearchBar()
{
@@ -33,19 +35,21 @@ QtSearchBar::QtSearchBar()
innerLayout->setContentsMargins(12, 3, 5, 2);
m_searchBoxContainer->setLayout(innerLayout);
m_searchBox = new QtSmartSearchBox(m_searchBoxContainer);
m_searchBox->setObjectName("search_box");
m_searchBox->setAttribute(Qt::WA_MacShowFocusRect, 0); // remove blue focus box on Mac
m_searchBox = new QtSmartSearchBox("Search", true, m_searchBoxContainer);
m_searchBox->setMinimumWidth(100);
m_searchBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
innerLayout->addWidget(m_searchBox);
connect(m_searchBox, &QtSmartSearchBox::autocomplete, this, &QtSearchBar::requestAutocomplete);
connect(m_searchBox, &QtSmartSearchBox::search, this, &QtSearchBar::requestSearch);
connect(m_searchBox, &QtSmartSearchBox::fullTextSearch, this, &QtSearchBar::requestFullTextSearch);
m_searchButton = new QtSearchBarButton(ResourcePaths::getGuiPath().concatenate(L"search_view/images/search.png"));
m_searchButton->setObjectName("search_button");
m_searchButton->setToolTip("search");
layout->addWidget(m_searchButton);
connect(m_searchButton, &QPushButton::clicked, m_searchBox, &QtSmartSearchBox::search);
connect(m_searchButton, &QPushButton::clicked, m_searchBox, &QtSmartSearchBox::startSearch);
refreshStyle();
}
@@ -98,3 +102,18 @@ void QtSearchBar::homeButtonClicked()
{
MessageActivateAll().dispatch();
}
void QtSearchBar::requestAutocomplete(const std::wstring& query, NodeTypeSet acceptedNodeTypes)
{
MessageSearchAutocomplete(query, acceptedNodeTypes).dispatch();
}
void QtSearchBar::requestSearch(const std::vector<SearchMatch>& matches, NodeTypeSet acceptedNodeTypes)
{
MessageSearch(matches, acceptedNodeTypes).dispatch();
}
void QtSearchBar::requestFullTextSearch(const std::wstring& query, bool caseSensitive)
{
MessageActivateFullTextSearch(query, caseSensitive).dispatch();
}
@@ -36,6 +36,10 @@ public:
private slots:
void homeButtonClicked();
void requestAutocomplete(const std::wstring& query, NodeTypeSet acceptedNodeTypes);
void requestSearch(const std::vector<SearchMatch>& matches, NodeTypeSet acceptedNodeTypes);
void requestFullTextSearch(const std::wstring& query, bool caseSensitive);
private:
QWidget* m_searchBoxContainer; // used for correct clipping inside the search box
@@ -11,9 +11,6 @@
#include "GraphViewStyle.h"
#include "NodeTypeSet.h"
#include "ColorScheme.h"
#include "MessageActivateFullTextSearch.h"
#include "MessageSearch.h"
#include "MessageSearchAutocomplete.h"
#include "utility.h"
#include "utilityString.h"
@@ -30,7 +27,7 @@ void QtSearchElement::onChecked(bool)
emit wasChecked(this);
}
void QtSmartSearchBox::search()
void QtSmartSearchBox::startSearch()
{
editTextToElement();
@@ -53,7 +50,7 @@ void QtSmartSearchBox::search()
else
{
QString text = QString::fromStdWString(match.name);
if (!text.startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER))
if (m_supportsFullTextSearch && !text.startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER))
{
text = QChar(SearchMatch::FULLTEXT_SEARCH_CHARACTER) + text;
}
@@ -62,46 +59,28 @@ void QtSmartSearchBox::search()
updateElements();
setEditText(text);
fullTextSearch();
startFullTextSearch();
return;
}
}
}
MessageSearch(utility::toVector(m_matches), getMatchAcceptedNodeTypes()).dispatch();
emit search(utility::toVector(m_matches), getMatchAcceptedNodeTypes());
}
void QtSmartSearchBox::fullTextSearch()
{
std::wstring term = text().toStdWString().substr(1);
if (term.empty())
{
return;
}
bool caseSensitive = false;
if (term.at(0) == SearchMatch::FULLTEXT_SEARCH_CHARACTER)
{
term = term.substr(1);
if (term.empty())
{
return;
}
caseSensitive = true;
}
MessageActivateFullTextSearch(term, caseSensitive).dispatch();
}
QtSmartSearchBox::QtSmartSearchBox(QWidget* parent)
QtSmartSearchBox::QtSmartSearchBox(const QString& placeholder, bool supportsFullTextSearch, QWidget* parent)
: QLineEdit(parent)
, m_placeholder(placeholder)
, m_supportsFullTextSearch(supportsFullTextSearch)
, m_allowTextChange(false)
, m_cursorIndex(0)
, m_shiftKeyDown(false)
, m_mousePressed(false)
, m_ignoreNextMousePress(false)
{
setObjectName("search_box");
setAttribute(Qt::WA_MacShowFocusRect, 0); // remove blue focus box on Mac
m_highlightRect = new QWidget(this);
m_highlightRect->setGeometry(0, 0, 0, 0);
m_highlightRect->setObjectName("search_box_highlight");
@@ -124,6 +103,11 @@ QCompleter* QtSmartSearchBox::getCompleter() const
return m_completer;
}
std::vector<SearchMatch> QtSmartSearchBox::getMatches() const
{
return utility::toVector(m_matches);
}
void QtSmartSearchBox::setAutocompletionList(const std::vector<SearchMatch>& autocompletionList)
{
// Save the cursor position, because after activating the completer the cursor gets set to the end position.
@@ -208,8 +192,8 @@ bool QtSmartSearchBox::event(QEvent *event)
setEditText(QString::fromStdWString(m_highlightedMatch.getFullName()));
requestAutoCompletions();
}
return true;
}
return true;
}
else if (keyEvent->key() == Qt::Key_Escape)
{
@@ -271,12 +255,12 @@ void QtSmartSearchBox::keyPressEvent(QKeyEvent* event)
{
if (text().startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER))
{
fullTextSearch();
startFullTextSearch();
return;
}
else if (!m_completer->popup()->isVisible())
{
search();
startSearch();
}
}
else if (event->key() == Qt::Key_Backspace)
@@ -646,7 +630,7 @@ void QtSmartSearchBox::onAutocompletionActivated(const SearchMatch& match)
if (!match.name.empty())
{
search();
startSearch();
}
}
@@ -1034,7 +1018,7 @@ void QtSmartSearchBox::updatePlaceholder()
{
if (text().isEmpty() && m_elements.empty())
{
setPlaceholderText("Search");
setPlaceholderText(m_placeholder);
}
else
{
@@ -1052,7 +1036,7 @@ void QtSmartSearchBox::requestAutoCompletions()
{
if (!text().isEmpty() && !text().startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER))
{
MessageSearchAutocomplete(text().toStdWString(), getMatchAcceptedNodeTypes()).dispatch();
emit autocomplete(text().toStdWString(), getMatchAcceptedNodeTypes());
}
else
{
@@ -1065,6 +1049,34 @@ void QtSmartSearchBox::hideAutoCompletions()
m_completer->popup()->hide();
}
void QtSmartSearchBox::startFullTextSearch()
{
if (!m_supportsFullTextSearch)
{
return;
}
std::wstring term = text().toStdWString().substr(1);
if (term.empty())
{
return;
}
bool caseSensitive = false;
if (term.at(0) == SearchMatch::FULLTEXT_SEARCH_CHARACTER)
{
term = term.substr(1);
if (term.empty())
{
return;
}
caseSensitive = true;
}
emit fullTextSearch(term, caseSensitive);
}
std::deque<SearchMatch> QtSmartSearchBox::getMatchesForInput(const std::wstring& text) const
{
std::deque<SearchMatch> matches;
@@ -33,15 +33,20 @@ class QtSmartSearchBox
{
Q_OBJECT
signals:
void autocomplete(const std::wstring& query, NodeTypeSet acceptedNodeTypes);
void search(const std::vector<SearchMatch>& matches, NodeTypeSet acceptedNodeTypes);
void fullTextSearch(const std::wstring& query, bool caseSensitive);
public slots:
void search();
void fullTextSearch();
void startSearch();
public:
QtSmartSearchBox(QWidget* parent);
QtSmartSearchBox(const QString& placeholder, bool supportsFullTextSearch, QWidget* parent = nullptr);
virtual ~QtSmartSearchBox();
QCompleter* getCompleter() const;
std::vector<SearchMatch> getMatches() const;
void setAutocompletionList(const std::vector<SearchMatch>& autocompletionList);
void setMatches(const std::vector<SearchMatch>& matches);
@@ -99,11 +104,16 @@ private:
void requestAutoCompletions();
void hideAutoCompletions();
void startFullTextSearch();
std::deque<SearchMatch> getMatchesForInput(const std::wstring& text) const;
NodeTypeSet getMatchAcceptedNodeTypes() const;
bool lastMatchIsNoFilter() const;
const QString m_placeholder;
const bool m_supportsFullTextSearch;
bool m_allowTextChange;
QString m_oldText;
@@ -120,20 +120,20 @@ QtGraphicsView::QtGraphicsView(QWidget* parent)
"", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/zoom_in.png"), "search/button", this);
m_zoomInButton->setObjectName("zoom_in_button");
m_zoomInButton->setAutoRepeat(true);
m_zoomInButton->setToolTip("Zoom in (" + modifierName + " + Mousewheel forward)");
m_zoomInButton->setToolTip("zoom in (" + modifierName + " + Mousewheel forward)");
connect(m_zoomInButton, &QPushButton::pressed, this, &QtGraphicsView::zoomInPressed);
m_zoomOutButton = new QtSelfRefreshIconButton(
"", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/zoom_out.png"), "search/button", this);
m_zoomOutButton->setObjectName("zoom_out_button");
m_zoomOutButton->setAutoRepeat(true);
m_zoomOutButton->setToolTip("Zoom out (" + modifierName + " + Mousewheel back)");
m_zoomOutButton->setToolTip("zoom out (" + modifierName + " + Mousewheel back)");
connect(m_zoomOutButton, &QPushButton::pressed, this, &QtGraphicsView::zoomOutPressed);
m_legendButton = new QtSelfRefreshIconButton(
"", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/legend.png"), "search/button", this);
m_legendButton->setObjectName("legend_button");
m_legendButton->setToolTip("Show graph legend");
m_legendButton->setToolTip("show legend");
connect(m_legendButton, &QPushButton::clicked, this, &QtGraphicsView::legendClicked);
}
+644
View File
@@ -0,0 +1,644 @@
#include "QtCustomTrailView.h"
#include <QBoxLayout>
#include <QButtonGroup>
#include <QCheckBox>
#include <QGridLayout>
#include <QGroupBox>
#include <QKeyEvent>
#include <QLabel>
#include <QRadioButton>
#include <QSlider>
#include "ColorScheme.h"
#include "MessageActivateTrail.h"
#include "NodeTypeSet.h"
#include "QtSmartSearchBox.h"
#include "ResourcePaths.h"
#include "TabId.h"
#include "utilityQt.h"
QtCustomTrailView::QtCustomTrailView(ViewLayout*)
: QWidget(nullptr)
, CustomTrailView(nullptr)
, m_controllerProxy(this, TabId::app())
{
setWindowTitle("Custom Trail");
QGridLayout* mainLayout = new QGridLayout();
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
setLayout(mainLayout);
QWidget* panelA1 = new QWidget();
QWidget* panelA2 = new QWidget();
QWidget* panelB1 = new QWidget();
QWidget* panelB2 = new QWidget();
QWidget* panelC1 = new QWidget();
QWidget* panelC2 = new QWidget();
QWidget* panelD = new QWidget();
panelA1->setObjectName("panelA");
panelA2->setObjectName("panelA");
panelB1->setObjectName("panelB");
panelB2->setObjectName("panelB");
panelC1->setObjectName("panelA");
panelC2->setObjectName("panelA");
panelD->setObjectName("panelB2");
mainLayout->addWidget(panelA1, 0, 0);
mainLayout->addWidget(panelA2, 0, 1);
mainLayout->addWidget(panelB1, 1, 0);
mainLayout->addWidget(panelB2, 1, 1);
mainLayout->addWidget(panelC1, 2, 0);
mainLayout->addWidget(panelC2, 2, 1);
mainLayout->addWidget(panelD, 3, 0, 1, 2);
// search boxes
{
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setContentsMargins(25, 10, 10, 10);
panelA1->setLayout(hLayout);
m_searchBoxFrom = new QtSmartSearchBox("Start Symbol", false);
m_searchBoxTo = new QtSmartSearchBox("Target Symbol", false);
hLayout->addWidget(new QLabel("From:"));
hLayout->addWidget(createSearchBox(m_searchBoxFrom));
hLayout->addSpacing(15);
QVBoxLayout* optionsLayout = new QVBoxLayout();
optionsLayout->setContentsMargins(10, 10, 25, 10);
panelA2->setLayout(optionsLayout);
m_optionTo = new QRadioButton(QString::fromUtf8("\xe2\x86\x92") + " To:");
m_optionReferenced = new QRadioButton(QString::fromUtf8("\xe2\x86\x92") + " All Referenced");
m_optionReferencing = new QRadioButton(QString::fromUtf8("\xe2\x86\x90") + " All Referencing");
m_optionTo->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
m_optionReferenced->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
m_optionReferencing->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
m_optionTo->setChecked(true);
QButtonGroup* options = new QButtonGroup();
options->addButton(m_optionTo);
options->addButton(m_optionReferenced);
options->addButton(m_optionReferencing);
QWidget* searchBoxToContainer = createSearchBox(m_searchBoxTo);
QHBoxLayout* toLayout = new QHBoxLayout();
toLayout->setContentsMargins(0, 0, 0, 0);
toLayout->addWidget(m_optionTo);
toLayout->addWidget(searchBoxToContainer);
optionsLayout->addLayout(toLayout);
optionsLayout->addWidget(m_optionReferenced);
optionsLayout->addSpacing(7);
optionsLayout->addWidget(m_optionReferencing);
connect(options, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked),
[this, searchBoxToContainer](QAbstractButton* button)
{
searchBoxToContainer->setEnabled(button == m_optionTo);
}
);
connect(m_searchBoxFrom, &QtSmartSearchBox::autocomplete,
[this](const std::wstring& query, NodeTypeSet acceptedNodeTypes)
{
m_controllerProxy.executeAsTaskWithArgs(&CustomTrailController::autocomplete, query, true);
}
);
connect(m_searchBoxTo, &QtSmartSearchBox::autocomplete,
[this](const std::wstring& query, NodeTypeSet acceptedNodeTypes)
{
m_controllerProxy.executeAsTaskWithArgs(&CustomTrailController::autocomplete, query, false);
}
);
}
// depth slider
{
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setContentsMargins(25, 10, 10, 10);
panelB1->setLayout(hLayout);
hLayout->addWidget(new QLabel("Max Depth:"));
m_slider = new QSlider(Qt::Horizontal);
m_slider->setObjectName("depth_slider");
m_slider->setToolTip("adjust graph depth");
m_slider->setMinimum(1);
m_slider->setMaximum(51);
m_slider->setMinimumWidth(150);
m_slider->setValue(INITIAL_GRAPH_DEPTH);
hLayout->addWidget(m_slider);
QLabel* valueLabel = new QLabel(QString::number(INITIAL_GRAPH_DEPTH));
valueLabel->setMinimumWidth(20);
hLayout->addWidget(valueLabel);
connect(m_slider, &QSlider::valueChanged,
[this, valueLabel](int)
{
if (m_slider->value() == m_slider->maximum())
{
valueLabel->setText("inf");
}
else
{
valueLabel->setText(QString::number(m_slider->value()));
}
}
);
hLayout->addStretch();
}
// layout direction
{
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setContentsMargins(10, 10, 25, 10);
hLayout->setSpacing(15);
panelB2->setLayout(hLayout);
m_horizontalButton = new QRadioButton("Horizontal");
m_verticalButton = new QRadioButton("Vertical");
m_horizontalButton->setChecked(true);
hLayout->addWidget(new QLabel("Layout Direction:"));
hLayout->addWidget(m_horizontalButton);
hLayout->addWidget(m_verticalButton);
QButtonGroup* layouts = new QButtonGroup();
layouts->addButton(m_horizontalButton);
layouts->addButton(m_verticalButton);
hLayout->addStretch();
}
ColorScheme* scheme = ColorScheme::getInstance().get();
// node filters
{
std::vector<QString> nodeFilters;
std::vector<QColor> nodeColors;
std::vector<NodeType::Type> nodeTypes = {
// NodeType::NODE_SYMBOL,
NodeType::NODE_TYPE,
NodeType::NODE_BUILTIN_TYPE,
// NodeType::NODE_MODULE,
// NodeType::NODE_NAMESPACE,
// NodeType::NODE_PACKAGE,
NodeType::NODE_CLASS,
NodeType::NODE_STRUCT,
NodeType::NODE_UNION,
NodeType::NODE_INTERFACE,
NodeType::NODE_TYPEDEF,
NodeType::NODE_TEMPLATE_PARAMETER,
NodeType::NODE_TYPE_PARAMETER,
NodeType::NODE_ENUM,
NodeType::NODE_ENUM_CONSTANT,
NodeType::NODE_GLOBAL_VARIABLE,
NodeType::NODE_FIELD,
NodeType::NODE_FUNCTION,
NodeType::NODE_METHOD,
NodeType::NODE_FILE,
NodeType::NODE_MACRO,
NodeType::NODE_ANNOTATION
};
for (NodeType::Type t : nodeTypes)
{
nodeFilters.push_back(QString::fromStdString(NodeType::getReadableTypeString(t)));
nodeColors.push_back(QColor(scheme->getNodeTypeColor(t, "fill", ColorScheme::FOCUS).c_str()));
}
QVBoxLayout* filterLayout = addFilters("Nodes:", nodeFilters, nodeColors, &m_nodeFilters, 11);
filterLayout->setContentsMargins(25, 10, 25, 10);
panelC1->setLayout(filterLayout);
}
// edge filters
{
std::vector<QString> edgeFilters;
std::vector<QColor> edgeColors;
std::vector<Edge::EdgeType> edgeTypes = {
// Edge::EDGE_UNDEFINED,
Edge::EDGE_TYPE_USAGE,
Edge::EDGE_INHERITANCE,
Edge::EDGE_USAGE,
Edge::EDGE_CALL,
Edge::EDGE_OVERRIDE,
// Edge::EDGE_TEMPLATE_ARGUMENT, // merged with type use
// Edge::EDGE_TEMPLATE_DEFAULT_ARGUMENT, // merged with type use
Edge::EDGE_TEMPLATE_SPECIALIZATION,
// Edge::EDGE_TEMPLATE_MEMBER_SPECIALIZATION, // merged with above
Edge::EDGE_TYPE_ARGUMENT,
Edge::EDGE_INCLUDE,
Edge::EDGE_IMPORT,
// Edge::EDGE_AGGREGATION,
Edge::EDGE_MACRO_USAGE,
Edge::EDGE_ANNOTATION_USAGE
// Edge::EDGE_MEMBER // has separate checkbox
};
for (Edge::EdgeType t : edgeTypes)
{
edgeFilters.push_back(QString::fromStdWString(Edge::getReadableTypeString(t)));
edgeColors.push_back(QColor(scheme->getEdgeTypeColor(t, ColorScheme::FOCUS).c_str()));
}
QVBoxLayout* filterLayout = addFilters("Edges:", edgeFilters, edgeColors, &m_edgeFilters, 5);
filterLayout->setContentsMargins(10, 10, 25, 10);
panelC2->setLayout(filterLayout);
}
// controls
{
QPushButton* cancelButton = new QPushButton("Cancel");
QPushButton* searchButton = new QPushButton("Search");
cancelButton->setObjectName("button");
searchButton->setObjectName("button");
cancelButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
searchButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac
m_errorLabel = new QLabel();
m_errorLabel->setObjectName("error");
QHBoxLayout* hLayout = new QHBoxLayout();
hLayout->setContentsMargins(25, 15, 25, 15);
hLayout->addWidget(cancelButton);
hLayout->addStretch();
hLayout->addWidget(m_errorLabel);
hLayout->addSpacing(10);
hLayout->addWidget(searchButton);
panelD->setLayout(hLayout);
connect(cancelButton, &QPushButton::clicked,
[this]()
{
hide();
}
);
connect(searchButton, &QPushButton::clicked,
[this]()
{
if (m_searchBoxFrom->getMatches().size() == 0 ||
m_searchBoxFrom->getMatches().front().tokenIds.size() == 0)
{
setError("No 'Start Symbol' symbol found.");
return;
}
Id startId = m_searchBoxFrom->getMatches().front().tokenIds.front();
Id endId = 0;
if (m_optionReferencing->isChecked())
{
std::swap(startId, endId);
}
else if (m_optionTo->isChecked())
{
if (m_searchBoxTo->getMatches().size() && m_searchBoxTo->getMatches().front().tokenIds.size())
{
endId = m_searchBoxTo->getMatches().front().tokenIds.front();
}
else
{
setError("No 'Target Symbol' symbol found.");
return;
}
}
NodeType::TypeMask nodeTypes = getCheckedNodeTypes();
Edge::TypeMask edgeTypes = getCheckedEdgeTypes();
if (!nodeTypes)
{
setError("No 'Nodes' selected.");
return;
}
if (!edgeTypes)
{
setError("No 'Edges' selected.");
return;
}
MessageActivateTrail message(
startId,
endId,
nodeTypes,
edgeTypes,
m_nodeNonIndexed->isChecked(),
m_slider->value() == m_slider->maximum() ? 0 : m_slider->value(),
m_horizontalButton->isChecked()
);
m_controllerProxy.executeAsTaskWithArgs(&CustomTrailController::activateTrail, message);
setError("");
hide();
}
);
}
}
void QtCustomTrailView::createWidgetWrapper()
{
}
void QtCustomTrailView::refreshView()
{
m_onQtThread([this]()
{
updateStyleSheet();
m_searchBoxFrom->refreshStyle();
m_searchBoxTo->refreshStyle();
});
}
void QtCustomTrailView::clearView()
{
m_onQtThread([this]()
{
m_searchBoxFrom->setMatches({});
m_searchBoxTo->setMatches({});
});
}
void QtCustomTrailView::setAvailableNodeAndEdgeTypes(NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes)
{
m_onQtThread([this, nodeTypes, edgeTypes]()
{
for (QCheckBox* filter : m_nodeFilters)
{
if (filter == m_nodeNonIndexed)
{
continue;
}
bool enabled = nodeTypes & NodeType::getTypeForReadableTypeString(filter->text().toStdWString());
filter->setEnabled(enabled);
filter->setVisible(enabled);
}
for (QCheckBox* filter : m_edgeFilters)
{
bool enabled = false;
if (filter == m_edgeMember)
{
enabled = edgeTypes & Edge::EDGE_MEMBER;
}
else
{
enabled = edgeTypes & Edge::getTypeForReadableTypeString(filter->text().toStdWString());
}
filter->setEnabled(enabled);
filter->setVisible(enabled);
}
});
}
void QtCustomTrailView::showView()
{
m_onQtThread([this]()
{
show();
raise();
});
}
void QtCustomTrailView::hideView()
{
m_onQtThread([this]()
{
hide();
});
}
void QtCustomTrailView::showAutocompletions(const std::vector<SearchMatch>& autocompletions, bool from)
{
m_onQtThread([this, autocompletions, from]()
{
if (from)
{
m_searchBoxFrom->setAutocompletionList(autocompletions);
}
else
{
m_searchBoxTo->setAutocompletionList(autocompletions);
}
});
}
void QtCustomTrailView::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Escape)
{
hide();
return;
}
QWidget::keyPressEvent(event);
}
void QtCustomTrailView::updateStyleSheet()
{
std::string css = utility::getStyleSheet(ResourcePaths::getGuiPath().concatenate(L"search_view/search_view.css"));
css += utility::getStyleSheet(ResourcePaths::getGuiPath().concatenate(L"custom_trail_view/custom_trail_view.css"));
setStyleSheet(css.c_str());
QAbstractItemView* popup = m_searchBoxFrom->getCompleter()->popup();
if (popup)
{
popup->setStyleSheet(css.c_str());
}
popup = m_searchBoxTo->getCompleter()->popup();
if (popup)
{
popup->setStyleSheet(css.c_str());
}
}
QWidget* QtCustomTrailView::createSearchBox(QtSmartSearchBox* searchBox) const
{
QWidget* searchBoxContainer = new QWidget();
searchBoxContainer->setObjectName("search_box_container");
searchBoxContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
searchBox->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Maximum);
QBoxLayout* innerLayout = new QVBoxLayout();
innerLayout->setContentsMargins(12, 3, 5, 2);
innerLayout->addWidget(searchBox);
searchBoxContainer->setLayout(innerLayout);
return searchBoxContainer;
}
QVBoxLayout* QtCustomTrailView::addFilters(
QString name,
const std::vector<QString>& filters,
const std::vector<QColor>& colors,
std::vector<QCheckBox*>* checkBoxes,
size_t filtersInFirstColumn)
{
QVBoxLayout* vLayout = new QVBoxLayout();
vLayout->addWidget(new QLabel(name));
QHBoxLayout* mainLayout = new QHBoxLayout();
vLayout->addLayout(mainLayout);
QVBoxLayout* filterALayout = new QVBoxLayout();
QVBoxLayout* filterBLayout = new QVBoxLayout();
mainLayout->addLayout(filterALayout);
mainLayout->addLayout(filterBLayout);
QPixmap pixmap(QString::fromStdString(ResourcePaths::getGuiPath().concatenate(L"custom_trail_view/images/circle.png").str()));
for (size_t i = 0; i < filters.size(); i++)
{
QCheckBox* checkBox = new QCheckBox(filters[i]);
checkBox->setChecked(true);
checkBox->setIcon(QIcon(utility::colorizePixmap(pixmap, colors[i])));
checkBoxes->push_back(checkBox);
if (i < filtersInFirstColumn)
{
filterALayout->addWidget(checkBox);
}
else
{
filterBLayout->addWidget(checkBox);
}
}
if (checkBoxes == &m_nodeFilters)
{
m_nodeNonIndexed = new QCheckBox("non-indexed");
m_nodeNonIndexed->setChecked(true);
checkBoxes->push_back(m_nodeNonIndexed);
filterBLayout->addSpacing(7);
filterBLayout->addWidget(m_nodeNonIndexed);
}
else if (checkBoxes == &m_edgeFilters)
{
m_edgeMember = new QCheckBox("member");
m_edgeMember->setChecked(true);
checkBoxes->push_back(m_edgeMember);
filterBLayout->addSpacing(5);
filterBLayout->addWidget(m_edgeMember);
}
filterALayout->addStretch();
filterBLayout->addStretch();
vLayout->addLayout(addCheckButtons(*checkBoxes));
vLayout->addStretch();
return vLayout;
}
QHBoxLayout* QtCustomTrailView::addCheckButtons(const std::vector<QCheckBox*>& checkBoxes) const
{
QHBoxLayout* buttonLayout = new QHBoxLayout();
QPushButton* checkButton = new QPushButton("Check All");
QPushButton* uncheckButton = new QPushButton("Uncheck All");
checkButton->setObjectName("button_small");
uncheckButton->setObjectName("button_small");
buttonLayout->addWidget(checkButton);
buttonLayout->addWidget(uncheckButton);
buttonLayout->addStretch();
connect(checkButton, &QPushButton::clicked,
[&checkBoxes]()
{
for (QCheckBox* box : checkBoxes)
{
if (box->isEnabled())
{
box->setChecked(true);
}
}
}
);
connect(uncheckButton, &QPushButton::clicked,
[&checkBoxes]()
{
for (QCheckBox* box : checkBoxes)
{
box->setChecked(false);
}
}
);
return buttonLayout;
}
NodeType::TypeMask QtCustomTrailView::getCheckedNodeTypes() const
{
NodeType::TypeMask nodeTypes = 0;
for (const QCheckBox* filter : m_nodeFilters)
{
if (filter->isEnabled() && filter->isChecked() && filter != m_nodeNonIndexed)
{
nodeTypes |= NodeType::getTypeForReadableTypeString(filter->text().toStdWString());
}
}
return nodeTypes;
}
Edge::TypeMask QtCustomTrailView::getCheckedEdgeTypes() const
{
Edge::TypeMask edgeTypes = 0;
for (const QCheckBox* filter : m_edgeFilters)
{
if (filter->isEnabled() && filter->isChecked() && filter != m_edgeMember)
{
Edge::EdgeType type = Edge::getTypeForReadableTypeString(filter->text().toStdWString());
edgeTypes |= type;
if (type == Edge::EDGE_TYPE_USAGE)
{
edgeTypes |= Edge::EDGE_TEMPLATE_ARGUMENT | Edge::EDGE_TEMPLATE_DEFAULT_ARGUMENT;
}
else if (type == Edge::EDGE_TEMPLATE_SPECIALIZATION)
{
edgeTypes |= Edge::EDGE_TEMPLATE_MEMBER_SPECIALIZATION;
}
}
}
if (m_edgeMember->isChecked())
{
edgeTypes |= Edge::EDGE_MEMBER;
}
return edgeTypes;
}
void QtCustomTrailView::setError(const QString& error)
{
m_errorLabel->setText(error);
}
+86
View File
@@ -0,0 +1,86 @@
#ifndef QT_CUSTOM_TRAIL_VIEW_H
#define QT_CUSTOM_TRAIL_VIEW_H
#include <QWidget>
#include "ControllerProxy.h"
#include "CustomTrailController.h"
#include "CustomTrailView.h"
#include "QtThreadedFunctor.h"
class QCheckBox;
class QHBoxLayout;
class QLabel;
class QSlider;
class QRadioButton;
class QtSmartSearchBox;
class QVBoxLayout;
class QtCustomTrailView
: public QWidget
, public CustomTrailView
{
public:
QtCustomTrailView(ViewLayout*);
// View implementation
void createWidgetWrapper() override;
void refreshView() override;
// TrailView implementation
void clearView() override;
void setAvailableNodeAndEdgeTypes(NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes) override;
void showView() override;
void hideView() override;
void showAutocompletions(const std::vector<SearchMatch>& autocompletions, bool from) override;
protected:
void keyPressEvent(QKeyEvent* event) override;
private:
void updateStyleSheet();
QWidget* createSearchBox(QtSmartSearchBox* searchBox) const;
QVBoxLayout* addFilters(
QString name,
const std::vector<QString>& filters,
const std::vector<QColor>& colors,
std::vector<QCheckBox*>* checkBoxes,
size_t filtersInFirstColumn);
QHBoxLayout* addCheckButtons(const std::vector<QCheckBox*>& checkBoxes) const;
NodeType::TypeMask getCheckedNodeTypes() const;
Edge::TypeMask getCheckedEdgeTypes() const;
void setError(const QString& error);
static const size_t INITIAL_GRAPH_DEPTH = 5;
static const size_t FILTERS_PER_COLUMN = 10;
QtThreadedLambdaFunctor m_onQtThread;
ControllerProxy<CustomTrailController> m_controllerProxy;
QtSmartSearchBox* m_searchBoxFrom;
QtSmartSearchBox* m_searchBoxTo;
QRadioButton* m_optionReferenced;
QRadioButton* m_optionReferencing;
QRadioButton* m_optionTo;
QSlider* m_slider;
QRadioButton* m_horizontalButton;
QRadioButton* m_verticalButton;
std::vector<QCheckBox*> m_nodeFilters;
std::vector<QCheckBox*> m_edgeFilters;
QCheckBox* m_nodeNonIndexed;
QCheckBox* m_edgeMember;
QLabel* m_errorLabel;
};
#endif // QT_CUSTOM_TRAIL_VIEW_H
+38 -24
View File
@@ -18,6 +18,7 @@
#include "DummyNode.h"
#include "GraphViewStyle.h"
#include "MessageActivateTrail.h"
#include "MessageCustomTrailShow.h"
#include "MessageDeactivateEdge.h"
#include "MessageRefreshUI.h"
#include "MessageScrollGraph.h"
@@ -81,7 +82,7 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout)
m_expandButton = new QtSelfRefreshIconButton(
"", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/graph.png"), "search/button");
m_expandButton->setObjectName("expand_button");
m_expandButton->setToolTip("show depth graph controls");
m_expandButton->setToolTip("show trail controls");
m_expandButton->setIconSize(QSize(16, 16));
m_expandButton->setGeometry(0, 0, 26, 26);
connect(m_expandButton, &QPushButton::clicked, this, &QtGraphView::clickedExpand);
@@ -96,10 +97,17 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout)
m_collapseButton = new QtSelfRefreshIconButton(
"", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/graph_arrow.png"), "search/button", ui);
m_collapseButton->setObjectName("collapse_button");
m_collapseButton->setToolTip("hide depth graph controls");
m_collapseButton->setToolTip("hide trail controls");
m_collapseButton->setIconSize(QSize(16, 16));
connect(m_collapseButton, &QPushButton::clicked, this, &QtGraphView::clickedCollapse);
m_customTrailButton = new QtSelfRefreshIconButton("", FilePath(), "search/button", ui);
m_customTrailButton->setObjectName("trail_button");
m_customTrailButton->setIconSize(QSize(16, 16));
m_customTrailButton->setToolTip("custom trail");
m_customTrailButton->setIconPath(ResourcePaths::getGuiPath().concatenate(L"graph_view/images/graph_custom.png"));
connect(m_customTrailButton, &QPushButton::clicked, this, &QtGraphView::clickedCustomTrail);
m_forwardTrailButton = new QtSelfRefreshIconButton("", FilePath(), "search/button", ui);
m_forwardTrailButton->setObjectName("trail_button");
m_forwardTrailButton->setIconSize(QSize(16, 16));
@@ -112,12 +120,12 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout)
m_trailDepthLabel = new QLabel(ui);
m_trailDepthLabel->setObjectName("depth_label");
m_trailDepthLabel->setToolTip("adjust graph depth");
m_trailDepthLabel->setToolTip("adjust trail depth");
m_trailDepthLabel->setAlignment(Qt::AlignCenter);
m_trailDepthSlider = new QSlider(Qt::Vertical, ui);
m_trailDepthSlider->setObjectName("depth_slider");
m_trailDepthSlider->setToolTip("adjust graph depth");
m_trailDepthSlider->setToolTip("adjust trail depth");
m_trailDepthSlider->setMinimum(1);
m_trailDepthSlider->setMaximum(26);
m_trailDepthSlider->setValue(5);
@@ -125,14 +133,15 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout)
connect(m_trailDepthSlider, &QSlider::sliderReleased, this, &QtGraphView::trailDepthUpdated);
m_collapseButton->setGeometry(0, 0, 26, 20);
m_backwardTrailButton->setGeometry(0, 22, 26, 26);
m_forwardTrailButton->setGeometry(0, 50, 26, 26);
m_trailDepthLabel->setGeometry(0, 78, 26, 26);
m_trailDepthSlider->setGeometry(0, 104, 26, 100);
m_customTrailButton->setGeometry(0, 22, 26, 26);
m_backwardTrailButton->setGeometry(0, 50, 26, 26);
m_forwardTrailButton->setGeometry(0, 78, 26, 26);
m_trailDepthLabel->setGeometry(0, 106, 26, 26);
m_trailDepthSlider->setGeometry(0, 132, 26, 100);
}
m_trailWidget = new QWidget(widget);
m_trailWidget->setGeometry(8, 8, 26, 210);
m_trailWidget->setGeometry(8, 8, 26, 238);
m_trailWidget->setLayout(stack);
if (ApplicationSettings::getInstance()->getGraphControlsVisible())
@@ -720,6 +729,11 @@ void QtGraphView::clickedExpand()
ApplicationSettings::getInstance()->save();
}
void QtGraphView::clickedCustomTrail()
{
MessageCustomTrailShow().dispatch();
}
void QtGraphView::clickedBackwardTrail()
{
activateTrail(false);
@@ -768,22 +782,22 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward)
QtGraphNodeData* node = dynamic_cast<QtGraphNodeData*>(m_oldActiveNode);
if (node)
{
Edge::TypeMask trailType;
Edge::TypeMask edgeTypes;
bool horizontalLayout = true;
if (node->getData()->getType().isInheritable())
{
trailType = Edge::EDGE_INHERITANCE | Edge::EDGE_TEMPLATE_SPECIALIZATION;
edgeTypes = Edge::EDGE_INHERITANCE | Edge::EDGE_TEMPLATE_SPECIALIZATION;
horizontalLayout = false;
}
else if (node->getData()->getType().isCallable())
{
trailType = Edge::EDGE_CALL | Edge::EDGE_OVERRIDE;
edgeTypes = Edge::EDGE_CALL | Edge::EDGE_OVERRIDE;
horizontalLayout = true;
}
else if (node->getData()->getType().isFile())
{
trailType = Edge::EDGE_INCLUDE;
edgeTypes = Edge::EDGE_INCLUDE;
horizontalLayout = true;
}
else
@@ -801,7 +815,7 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward)
depth = 0;
}
return MessageActivateTrail(originId, targetId, trailType, depth, horizontalLayout);
return MessageActivateTrail(originId, targetId, edgeTypes, depth, horizontalLayout);
}
return message;
@@ -810,7 +824,7 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward)
void QtGraphView::activateTrail(bool forward)
{
MessageActivateTrail message = getMessageActivateTrail(forward);
if (message.trailType)
if (message.edgeTypes)
{
message.dispatch();
}
@@ -820,20 +834,20 @@ void QtGraphView::updateTrailButtons()
{
MessageActivateTrail message = getMessageActivateTrail(false);
m_backwardTrailButton->setEnabled(message.trailType);
m_forwardTrailButton->setEnabled(message.trailType);
m_trailDepthLabel->setEnabled(message.trailType);
m_trailDepthSlider->setEnabled(message.trailType);
m_backwardTrailButton->setEnabled(message.edgeTypes);
m_forwardTrailButton->setEnabled(message.edgeTypes);
m_trailDepthLabel->setEnabled(message.edgeTypes);
m_trailDepthSlider->setEnabled(message.edgeTypes);
std::wstring backwardImagePath = L"graph_left.png";
std::wstring forwardImagePath = L"graph_right.png";
if (message.trailType & Edge::EDGE_CALL)
if (message.edgeTypes & Edge::EDGE_CALL)
{
m_backwardTrailButton->setToolTip("show caller graph");
m_forwardTrailButton->setToolTip("show callee graph");
}
else if (message.trailType & Edge::EDGE_INHERITANCE)
else if (message.edgeTypes & Edge::EDGE_INHERITANCE)
{
m_backwardTrailButton->setToolTip("show base hierarchy");
m_forwardTrailButton->setToolTip("show derived hierarchy");
@@ -841,15 +855,15 @@ void QtGraphView::updateTrailButtons()
backwardImagePath = L"graph_up.png";
forwardImagePath = L"graph_down.png";
}
else if (message.trailType & Edge::EDGE_INCLUDE)
else if (message.edgeTypes & Edge::EDGE_INCLUDE)
{
m_backwardTrailButton->setToolTip("show including files hierarchy");
m_forwardTrailButton->setToolTip("show included files hierarchy");
}
else
{
m_backwardTrailButton->setToolTip("no depth graph available for active symbol");
m_forwardTrailButton->setToolTip("no depth graph available for active symbol");
m_backwardTrailButton->setToolTip("no trail for active symbol");
m_forwardTrailButton->setToolTip("no trail for active symbol");
}
m_forwardTrailButton->setIconPath(ResourcePaths::getGuiPath().concatenate(L"graph_view/images/" + forwardImagePath));
+2
View File
@@ -81,6 +81,7 @@ private slots:
void clickedCollapse();
void clickedExpand();
void clickedCustomTrail();
void clickedBackwardTrail();
void clickedForwardTrail();
@@ -152,6 +153,7 @@ private:
QWidget* m_trailWidget;
QtSelfRefreshIconButton* m_expandButton;
QtSelfRefreshIconButton* m_collapseButton;
QtSelfRefreshIconButton* m_customTrailButton;
QtSelfRefreshIconButton* m_forwardTrailButton;
QtSelfRefreshIconButton* m_backwardTrailButton;
QSlider* m_trailDepthSlider;
+6
View File
@@ -5,6 +5,7 @@
#include "QtBookmarkView.h"
#include "QtCodeView.h"
#include "QtCompositeView.h"
#include "QtCustomTrailView.h"
#include "QtDialogView.h"
#include "QtErrorView.h"
#include "QtGraphView.h"
@@ -51,6 +52,11 @@ std::shared_ptr<CodeView> QtViewFactory::createCodeView(ViewLayout* viewLayout)
return View::createAndAddToLayout<QtCodeView>(viewLayout);
}
std::shared_ptr<CustomTrailView> QtViewFactory::createCustomTrailView() const
{
return View::create<QtCustomTrailView>(nullptr);
}
std::shared_ptr<ErrorView> QtViewFactory::createErrorView(ViewLayout* viewLayout) const
{
return View::createAndAddToLayout<QtErrorView>(viewLayout);
+1
View File
@@ -18,6 +18,7 @@ public:
std::shared_ptr<BookmarkButtonsView> createBookmarkButtonsView(ViewLayout* viewLayout) const override;
std::shared_ptr<BookmarkView> createBookmarkView(ViewLayout* viewLayout) const override;
std::shared_ptr<CodeView> createCodeView(ViewLayout* viewLayout) const override;
std::shared_ptr<CustomTrailView> createCustomTrailView() const override;
std::shared_ptr<ErrorView> createErrorView(ViewLayout* viewLayout) const override;
std::shared_ptr<GraphView> createGraphView(ViewLayout* viewLayout) const override;
std::shared_ptr<RefreshView> createRefreshView(ViewLayout* viewLayout) const override;
+10
View File
@@ -40,6 +40,7 @@
#include "MessageBookmarkBrowse.h"
#include "MessageBookmarkCreate.h"
#include "MessageCodeReference.h"
#include "MessageCustomTrailShow.h"
#include "MessageFind.h"
#include "MessageIndexingShowDialog.h"
#include "MessageLoadProject.h"
@@ -755,6 +756,11 @@ void QtMainWindow::codeLocalReferenceNext()
MessageCodeReference(MessageCodeReference::REFERENCE_NEXT, true).dispatch();
}
void QtMainWindow::customTrail()
{
MessageCustomTrailShow().dispatch();
}
void QtMainWindow::overview()
{
MessageActivateAll().dispatch();
@@ -963,6 +969,10 @@ void QtMainWindow::setupEditMenu()
menu->addSeparator();
menu->addAction(tr("Custom Trail..."), this, &QtMainWindow::customTrail, QKeySequence(Qt::CTRL + Qt::Key_L));
menu->addSeparator();
menu->addAction(tr("&To overview"), this, &QtMainWindow::overview, QKeySequence::MoveToStartOfDocument);
menu->addSeparator();
+1
View File
@@ -137,6 +137,7 @@ public slots:
void codeReferenceNext();
void codeLocalReferencePrevious();
void codeLocalReferenceNext();
void customTrail();
void overview();
void closeWindow();