The editor now has context aware completions
This commit is contained in:
parent
1a1bbd0838
commit
960967c8a6
18 changed files with 1984 additions and 0 deletions
112
pglab/codeeditor/CompletionPopup.cpp
Normal file
112
pglab/codeeditor/CompletionPopup.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#include "CompletionPopup.h"
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int DetailRole = Qt::UserRole + 1;
|
||||
constexpr int AmbiguousRole = Qt::UserRole + 2;
|
||||
|
||||
class CompletionDelegate : public QStyledItemDelegate {
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
const QString display = opt.text;
|
||||
opt.text.clear();
|
||||
const QWidget *w = opt.widget;
|
||||
(w ? w->style() : QApplication::style())->drawControl(QStyle::CE_ItemViewItem, &opt, painter, w);
|
||||
|
||||
const bool selected = opt.state & QStyle::State_Selected;
|
||||
const QPalette &pal = opt.palette;
|
||||
const QString detail = index.data(DetailRole).toString();
|
||||
const bool ambiguous = index.data(AmbiguousRole).toBool();
|
||||
|
||||
painter->save();
|
||||
painter->setPen(selected ? pal.highlightedText().color() : pal.text().color());
|
||||
QRect r = opt.rect.adjusted(4, 0, -4, 0);
|
||||
painter->drawText(r, Qt::AlignVCenter | Qt::AlignLeft, display);
|
||||
if (!detail.isEmpty()) {
|
||||
const int x = painter->fontMetrics().horizontalAdvance(display) + 12;
|
||||
QColor dim = selected ? pal.highlightedText().color() : pal.color(QPalette::Disabled, QPalette::Text);
|
||||
painter->setPen(dim);
|
||||
if (ambiguous) {
|
||||
QFont f = painter->font();
|
||||
f.setBold(true);
|
||||
painter->setFont(f);
|
||||
}
|
||||
painter->drawText(r.adjusted(x, 0, 0, 0), Qt::AlignVCenter | Qt::AlignLeft, QStringLiteral("\u2014 ") + detail);
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
|
||||
{
|
||||
QSize s = QStyledItemDelegate::sizeHint(option, index);
|
||||
const QFontMetrics fm(option.font);
|
||||
s.setWidth(fm.horizontalAdvance(index.data(Qt::DisplayRole).toString())
|
||||
+ fm.horizontalAdvance(index.data(DetailRole).toString()) + 40);
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CompletionPopup::CompletionPopup(QWidget *parent)
|
||||
: QListWidget(parent)
|
||||
{
|
||||
setWindowFlags(Qt::ToolTip);
|
||||
setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setUniformItemSizes(true);
|
||||
setItemDelegate(new CompletionDelegate(this));
|
||||
connect(this, &QListWidget::itemClicked, this, [this](QListWidgetItem *item) {
|
||||
emit itemAccepted(row(item));
|
||||
});
|
||||
}
|
||||
|
||||
void CompletionPopup::setItems(const std::vector<EditorCompletionItem> &items)
|
||||
{
|
||||
clear();
|
||||
int width = 150;
|
||||
const QFontMetrics fm(font());
|
||||
for (const auto &i : items) {
|
||||
auto *item = new QListWidgetItem(i.display, this);
|
||||
item->setData(DetailRole, i.detail);
|
||||
item->setData(AmbiguousRole, i.ambiguous);
|
||||
width = std::max(width, fm.horizontalAdvance(i.display) + fm.horizontalAdvance(i.detail) + 50);
|
||||
}
|
||||
if (!items.empty())
|
||||
setCurrentRow(0);
|
||||
const int rows = static_cast<int>(std::min<size_t>(items.size(), 10));
|
||||
const int rowHeight = fm.height() + 6;
|
||||
resize(std::min(width, 600), rows * rowHeight + 2 * frameWidth() + 2);
|
||||
}
|
||||
|
||||
void CompletionPopup::moveSelection(int delta)
|
||||
{
|
||||
const int n = count();
|
||||
if (n == 0)
|
||||
return;
|
||||
int r = QListWidget::currentRow() + delta;
|
||||
r = std::clamp(r, 0, n - 1);
|
||||
setCurrentRow(r);
|
||||
}
|
||||
|
||||
void CompletionPopup::showAt(const QPoint &globalPos)
|
||||
{
|
||||
QPoint p = globalPos;
|
||||
if (auto *screen = QApplication::screenAt(globalPos)) {
|
||||
const QRect avail = screen->availableGeometry();
|
||||
if (p.x() + width() > avail.right())
|
||||
p.setX(std::max(avail.left(), avail.right() - width()));
|
||||
}
|
||||
move(p);
|
||||
show();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue