ui: augmented autocompletion list
This change adds search match information to the autocompletion list: - matching letters - color for token or command - node type as string
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
/build/
|
||||
|
||||
/bin/app/Debug/
|
||||
/bin/app/Release/
|
||||
/bin/app/data/log/
|
||||
|
||||
/bin/lib/
|
||||
|
||||
/bin/test/Debug/
|
||||
/bin/test/Release/
|
||||
/bin/test/data/log/
|
||||
/bin/test/data/temp.xml
|
||||
|
||||
/bin/app/data/window_settings.ini
|
||||
/bin/app/data/ApplicationSettings.xml
|
||||
|
||||
@@ -5,6 +5,8 @@ add_files(
|
||||
platform_includes/includesMac.h
|
||||
platform_includes/includesWindows.h
|
||||
|
||||
qt/element/QtAutocompletionList.cpp
|
||||
qt/element/QtAutocompletionList.h
|
||||
qt/element/QtCodeFile.cpp
|
||||
qt/element/QtCodeFile.h
|
||||
qt/element/QtCodeFileList.cpp
|
||||
@@ -30,7 +32,7 @@ add_files(
|
||||
qt/view/graphElements/nodeComponents/QtGraphNodeComponentMoveable.h
|
||||
qt/view/graphElements/nodeComponents/QtGraphNodeComponentToggleButton.cpp
|
||||
qt/view/graphElements/nodeComponents/QtGraphNodeComponentToggleButton.h
|
||||
|
||||
|
||||
qt/view/graphElements/QtGraphEdge.cpp
|
||||
qt/view/graphElements/QtGraphEdge.h
|
||||
qt/view/graphElements/QtGraphNode.cpp
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "qt/element/QtAutocompletionList.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
QtAutocompletionModel::QtAutocompletionModel(const std::vector<SearchMatch>& matchList, QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
, m_matchList(matchList)
|
||||
{
|
||||
}
|
||||
|
||||
QtAutocompletionModel::~QtAutocompletionModel()
|
||||
{
|
||||
}
|
||||
|
||||
int QtAutocompletionModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return m_matchList.size();
|
||||
}
|
||||
|
||||
int QtAutocompletionModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
return 3;
|
||||
}
|
||||
|
||||
QVariant QtAutocompletionModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid() || index.row() < 0 || size_t(index.row()) >= m_matchList.size() || role != Qt::DisplayRole)
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
const SearchMatch& match = m_matchList[index.row()];
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case 0:
|
||||
return QString::fromStdString(match.fullName);
|
||||
case 1:
|
||||
return QString::fromStdString(match.typeName);
|
||||
case 2:
|
||||
{
|
||||
QList<QVariant> indices;
|
||||
for (const size_t idx : match.indices)
|
||||
{
|
||||
indices.push_back(quint64(idx));
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
const SearchMatch* QtAutocompletionModel::getSearchMatchAt(int idx) const
|
||||
{
|
||||
if (idx >= 0 && size_t(idx) < m_matchList.size())
|
||||
{
|
||||
return &m_matchList[idx];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
QtAutocompletionDelegate::QtAutocompletionDelegate(QObject* parent)
|
||||
: QItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QtAutocompletionDelegate::~QtAutocompletionDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
void QtAutocompletionDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
QPen pen = painter->pen();
|
||||
QFont font = painter->font();
|
||||
|
||||
if (option.state & QStyle::State_Selected)
|
||||
{
|
||||
QPen highlightPen = pen;
|
||||
highlightPen.setColor(option.palette.color(QPalette::HighlightedText));
|
||||
painter->setPen(highlightPen);
|
||||
|
||||
painter->fillRect(option.rect, option.palette.color(QPalette::Highlight));
|
||||
}
|
||||
else
|
||||
{
|
||||
painter->fillRect(option.rect, option.palette.color(QPalette::Base));
|
||||
}
|
||||
|
||||
QFont higlightFont = font;
|
||||
higlightFont.setWeight(QFont::Bold);
|
||||
|
||||
QString name = index.data().toString();
|
||||
QList<QVariant> indices = index.sibling(index.row(), index.column() + 2).data().toList();
|
||||
int idx = 0;
|
||||
|
||||
int x = 0;
|
||||
int m = option.fontMetrics.width(QLatin1Char('9'));
|
||||
for (int i = 0; i < name.size(); i++)
|
||||
{
|
||||
if (idx < indices.size() && i == indices[idx])
|
||||
{
|
||||
painter->setFont(higlightFont);
|
||||
idx++;
|
||||
}
|
||||
else
|
||||
{
|
||||
painter->setFont(font);
|
||||
}
|
||||
|
||||
painter->drawText(option.rect.adjusted(8 + x, 0, 0, 0), Qt::AlignLeft, name.at(i));
|
||||
x += m;
|
||||
}
|
||||
|
||||
if (font.pixelSize() > 0)
|
||||
{
|
||||
QFont typeFont = font;
|
||||
typeFont.setPixelSize(0.8f * font.pixelSize());
|
||||
painter->setFont(typeFont);
|
||||
}
|
||||
|
||||
QString type = index.sibling(index.row(), index.column() + 1).data().toString();
|
||||
QRect rect = option.rect.adjusted(1, 1, 0, -1);
|
||||
rect.setWidth(5);
|
||||
|
||||
if (type.size())
|
||||
{
|
||||
painter->fillRect(rect, QColor(153, 22, 165));
|
||||
painter->drawText(option.rect.adjusted(0, 0, -3, 0), Qt::AlignRight, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter->fillRect(rect, QColor(172, 150, 0));
|
||||
}
|
||||
|
||||
painter->setFont(font);
|
||||
painter->setPen(pen);
|
||||
}
|
||||
|
||||
|
||||
QtAutocompletionList::QtAutocompletionList(const std::vector<SearchMatch>& autocompletionList, QWidget* parent)
|
||||
: QCompleter(parent)
|
||||
{
|
||||
m_model = std::make_shared<QtAutocompletionModel>(autocompletionList, this);
|
||||
setModel(m_model.get());
|
||||
|
||||
m_delegate = std::make_shared<QtAutocompletionDelegate>(this);
|
||||
|
||||
QListView* list = new QListView(parent);
|
||||
list->setItemDelegateForColumn(0, m_delegate.get());
|
||||
list->setObjectName("search_box_popup");
|
||||
setPopup(list);
|
||||
|
||||
setCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
connect(this, SIGNAL(highlighted(const QModelIndex&)), this, SLOT(onHighlighted(const QModelIndex&)), Qt::DirectConnection);
|
||||
connect(this, SIGNAL(activated(const QModelIndex&)), this, SLOT(onActivated(const QModelIndex&)), Qt::DirectConnection);
|
||||
}
|
||||
|
||||
QtAutocompletionList::~QtAutocompletionList()
|
||||
{
|
||||
}
|
||||
|
||||
const SearchMatch* QtAutocompletionList::getSearchMatchAt(int idx) const
|
||||
{
|
||||
return m_model->getSearchMatchAt(idx);
|
||||
}
|
||||
|
||||
void QtAutocompletionList::onHighlighted(const QModelIndex& index)
|
||||
{
|
||||
const SearchMatch* match = getSearchMatchAt(index.row());
|
||||
if (match)
|
||||
{
|
||||
emit matchHighlighted(*match);
|
||||
}
|
||||
}
|
||||
|
||||
void QtAutocompletionList::onActivated(const QModelIndex& index)
|
||||
{
|
||||
const SearchMatch* match = getSearchMatchAt(index.row());
|
||||
if (match)
|
||||
{
|
||||
emit matchActivated(*match);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef QT_AUTOCOMPLETION_LIST
|
||||
#define QT_AUTOCOMPLETION_LIST
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QCompleter>
|
||||
#include <QItemDelegate>
|
||||
#include <QListView>
|
||||
|
||||
#include "data/search/SearchMatch.h"
|
||||
|
||||
class QtAutocompletionModel
|
||||
: public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtAutocompletionModel(const std::vector<SearchMatch>& matchList, QObject* parent = 0);
|
||||
virtual ~QtAutocompletionModel();
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent) const;
|
||||
virtual int columnCount(const QModelIndex& parent) const;
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role) const;
|
||||
|
||||
const SearchMatch* getSearchMatchAt(int idx) const;
|
||||
|
||||
private:
|
||||
const std::vector<SearchMatch>& m_matchList;
|
||||
};
|
||||
|
||||
|
||||
class QtAutocompletionDelegate
|
||||
: public QItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit QtAutocompletionDelegate(QObject* parent = 0);
|
||||
virtual ~QtAutocompletionDelegate();
|
||||
|
||||
virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
||||
};
|
||||
|
||||
|
||||
class QtAutocompletionList
|
||||
: public QCompleter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void matchHighlighted(const SearchMatch&);
|
||||
void matchActivated(const SearchMatch&);
|
||||
|
||||
public:
|
||||
QtAutocompletionList(const std::vector<SearchMatch>& autocompletionList, QWidget* parent = 0);
|
||||
virtual ~QtAutocompletionList();
|
||||
|
||||
const SearchMatch* getSearchMatchAt(int idx) const;
|
||||
|
||||
private slots:
|
||||
void onHighlighted(const QModelIndex& index);
|
||||
void onActivated(const QModelIndex& index);
|
||||
|
||||
private:
|
||||
std::shared_ptr<QtAutocompletionModel> m_model;
|
||||
std::shared_ptr<QtAutocompletionDelegate> m_delegate;
|
||||
};
|
||||
|
||||
#endif // QT_AUTOCOMPLETION_LIST
|
||||
@@ -2,18 +2,18 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QCompleter>
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "data/query/QueryTree.h"
|
||||
#include "utility/messaging/type/MessageSearch.h"
|
||||
#include "utility/messaging/type/MessageSearchAutocomplete.h"
|
||||
#include "utility/text/TextAccess.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
#include "data/query/QueryTree.h"
|
||||
#include "qt/element/QtAutocompletionList.h"
|
||||
|
||||
QtQueryElement::QtQueryElement(const QString& text, QWidget* parent)
|
||||
: QPushButton(text, parent)
|
||||
{
|
||||
@@ -54,29 +54,18 @@ QtSmartSearchBox::~QtSmartSearchBox()
|
||||
|
||||
void QtSmartSearchBox::setAutocompletionList(const std::vector<SearchMatch>& autocompletionList)
|
||||
{
|
||||
m_matches = autocompletionList;
|
||||
|
||||
if (!m_matches.size())
|
||||
if (!autocompletionList.size())
|
||||
{
|
||||
setCompleter(0);
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList wordList;
|
||||
for (const SearchMatch& match: autocompletionList)
|
||||
{
|
||||
wordList << match.fullName.c_str();
|
||||
}
|
||||
|
||||
QCompleter *completer = new QCompleter(wordList, this);
|
||||
completer->popup()->setObjectName("search_box_popup");
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
QCompleter* completer = new QtAutocompletionList(autocompletionList, this);
|
||||
setCompleter(completer);
|
||||
completer->complete();
|
||||
completer->complete(QRect(textMargins().left() + 3, height(), 300, 1));
|
||||
|
||||
connect(completer, SIGNAL(highlighted(const QModelIndex&)), this, SLOT(onSearchCompletionHighlighted(const QModelIndex&)), Qt::DirectConnection);
|
||||
connect(completer, SIGNAL(activated(const QModelIndex&)), this, SLOT(onSearchCompletionActivated(const QModelIndex&)), Qt::DirectConnection);
|
||||
connect(completer, SIGNAL(matchHighlighted(const SearchMatch&)), this, SLOT(onAutocompletionHighlighted(const SearchMatch&)), Qt::DirectConnection);
|
||||
connect(completer, SIGNAL(matchActivated(const SearchMatch&)), this, SLOT(onAutocompletionActivated(const SearchMatch&)), Qt::DirectConnection);
|
||||
|
||||
completer->popup()->setCurrentIndex(completer->completionModel()->index(0, 0));
|
||||
}
|
||||
@@ -96,6 +85,21 @@ void QtSmartSearchBox::setFocus()
|
||||
selectAllElementsWith(true);
|
||||
}
|
||||
|
||||
bool QtSmartSearchBox::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress)
|
||||
{
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
|
||||
if (keyEvent->key() == Qt::Key_Tab && completer() && completer()->popup()->isVisible())
|
||||
{
|
||||
onAutocompletionActivated(m_highlightedMatch);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
void QtSmartSearchBox::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QLineEdit::resizeEvent(event);
|
||||
@@ -371,19 +375,20 @@ void QtSmartSearchBox::onTextChanged(const QString& text)
|
||||
updatePlaceholder();
|
||||
}
|
||||
|
||||
void QtSmartSearchBox::onSearchCompletionHighlighted(const QModelIndex& index)
|
||||
void QtSmartSearchBox::onAutocompletionHighlighted(const SearchMatch& match)
|
||||
{
|
||||
m_highlightedMatch = match;
|
||||
}
|
||||
|
||||
void QtSmartSearchBox::onSearchCompletionActivated(const QModelIndex& index)
|
||||
void QtSmartSearchBox::onAutocompletionActivated(const SearchMatch& match)
|
||||
{
|
||||
if (index.row() >= 0 && index.row() < int(m_matches.size()))
|
||||
if (match.fullName.size())
|
||||
{
|
||||
m_oldText.clear();
|
||||
clearLineEdit();
|
||||
|
||||
std::string match = m_matches[index.row()].encodeForQuery();
|
||||
textToToken(match);
|
||||
std::string name = match.encodeForQuery();
|
||||
textToToken(name);
|
||||
updateElements();
|
||||
}
|
||||
|
||||
@@ -457,9 +462,13 @@ void QtSmartSearchBox::textToToken(std::string text)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_matches.size() && utility::equalsCaseInsensitive(text, m_matches.front().fullName))
|
||||
if (completer())
|
||||
{
|
||||
text = m_matches.front().encodeForQuery();
|
||||
const SearchMatch* match = dynamic_cast<QtAutocompletionList*>(completer())->getSearchMatchAt(0);
|
||||
if (match && utility::equalsCaseInsensitive(text, match->fullName))
|
||||
{
|
||||
text = match->encodeForQuery();
|
||||
}
|
||||
}
|
||||
|
||||
m_tokens.insert(m_tokens.begin() + m_cursorIndex, text);
|
||||
|
||||
@@ -43,6 +43,7 @@ public:
|
||||
void setFocus();
|
||||
|
||||
protected:
|
||||
virtual bool event(QEvent *event);
|
||||
virtual void resizeEvent(QResizeEvent* event);
|
||||
virtual void keyPressEvent(QKeyEvent* event);
|
||||
virtual void keyReleaseEvent(QKeyEvent* event);
|
||||
@@ -55,8 +56,8 @@ private slots:
|
||||
void onTextEdited(const QString& text);
|
||||
void onTextChanged(const QString& text);
|
||||
|
||||
void onSearchCompletionHighlighted(const QModelIndex& index);
|
||||
void onSearchCompletionActivated(const QModelIndex& index);
|
||||
void onAutocompletionHighlighted(const SearchMatch& match);
|
||||
void onAutocompletionActivated(const SearchMatch& match);
|
||||
|
||||
void onElementSelected(QtQueryElement* element);
|
||||
|
||||
@@ -91,7 +92,7 @@ private:
|
||||
|
||||
size_t m_cursorIndex;
|
||||
|
||||
std::vector<SearchMatch> m_matches;
|
||||
SearchMatch m_highlightedMatch;
|
||||
|
||||
bool m_shiftKeyDown;
|
||||
bool m_mousePressed;
|
||||
|
||||
@@ -20,7 +20,7 @@ std::vector<std::string> ApplicationSettings::getHeaderSearchPaths() const
|
||||
{
|
||||
//TODO: defaultValues?
|
||||
std::vector<std::string> defaultValues;
|
||||
return getValues("source/HeaderSearchPaths", defaultValues);
|
||||
return getValues("source/HeaderSearchPaths/HeaderSearchPath", defaultValues);
|
||||
}
|
||||
|
||||
int ApplicationSettings::getCodeTabWidth() const
|
||||
|
||||
@@ -376,6 +376,18 @@ std::vector<SearchMatch> Storage::getAutocompletionMatches(
|
||||
|
||||
std::vector<SearchMatch> matches = SearchIndex::getMatches(tokenResults, word);
|
||||
SearchMatch::log(matches, word);
|
||||
|
||||
for (SearchMatch& match : matches)
|
||||
{
|
||||
if (!match.tokenIds.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Token* token = m_graph.getTokenById(*match.tokenIds.cbegin());
|
||||
match.typeName = token->getTypeString();
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
// Logging.
|
||||
std::string getTypeString(EdgeType type) const;
|
||||
std::string getTypeString() const;
|
||||
virtual std::string getTypeString() const;
|
||||
std::string getAsString() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
|
||||
// Logging.
|
||||
std::string getTypeString(NodeType type) const;
|
||||
std::string getTypeString() const;
|
||||
virtual std::string getTypeString() const;
|
||||
std::string getAsString() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -31,6 +31,9 @@ public:
|
||||
template <typename ComponentType>
|
||||
std::shared_ptr<ComponentType> removeComponent();
|
||||
|
||||
// Logging.
|
||||
virtual std::string getTypeString() const = 0;
|
||||
|
||||
protected:
|
||||
Token(const Token& other);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ struct SearchMatch
|
||||
std::string encodeForQuery() const;
|
||||
|
||||
std::string fullName;
|
||||
std::string typeName;
|
||||
std::set<Id> tokenIds;
|
||||
std::vector<size_t> indices;
|
||||
size_t weight;
|
||||
|
||||
@@ -115,8 +115,8 @@ public:
|
||||
void test_config_manager_save_and_load_configuration_and_compare()
|
||||
{
|
||||
std::shared_ptr<ConfigManager> config = ConfigManager::createAndLoad(getConfigTextAccess());
|
||||
config->save("temp.xml");
|
||||
std::shared_ptr<ConfigManager> config2 = ConfigManager::createAndLoad(TextAccess::createFromFile("temp.xml"));
|
||||
config->save("data/temp.xml");
|
||||
std::shared_ptr<ConfigManager> config2 = ConfigManager::createAndLoad(TextAccess::createFromFile("data/temp.xml"));
|
||||
TS_ASSERT_EQUALS(config->toString(), config2->toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -326,6 +326,11 @@ private:
|
||||
{
|
||||
return Token::removeComponent<ComponentType>();
|
||||
}
|
||||
|
||||
virtual std::string getTypeString() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
class TestComponent: public TokenComponent
|
||||
|
||||
Reference in New Issue
Block a user