Big QCodeEdit update: supports for autocomplete/help hint and custom data. Add dynamic links on Ctrl+mouse by 2 new virtual functions.

This commit is contained in:
2020-09-12 01:08:13 +03:00
parent be2870e116
commit aa695f8494
5 changed files with 144 additions and 45 deletions

View File

@@ -18,9 +18,11 @@
#include "ui_qcodeedit.h"
Q_DECLARE_METATYPE(QTextCursor)
Q_DECLARE_METATYPE(QCodeEdit::ACEntry)
QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) {
qRegisterMetaType<QTextCursor>();
qRegisterMetaType<QCodeEdit::ACEntry>();
ui = new Ui::QCodeEdit();
ui->setupUi(this);
ui->widgetSearch->hide();
@@ -38,6 +40,8 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) {
es_search.format.setBackground(QColor(255, 240, 10));
es_range.format.setBackground(QColor(230, 246, 255));
es_range.format.setProperty(QTextFormat::FullWidthSelection, true);
es_link.format.setForeground(Qt::blue);
es_link.format.setFontUnderline(true);
widget_help = new QFrame();
widget_help->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
widget_help->setFocusPolicy(Qt::NoFocus);
@@ -45,18 +49,19 @@ QCodeEdit::QCodeEdit(QWidget * parent): QWidget(parent) {
widget_help->setFrameShape(QFrame::StyledPanel);
widget_help->setLayout(new QBoxLayout(QBoxLayout::TopToBottom));
widget_help->layout()->setContentsMargins(0, 0, 0, 0);
for (int i = 0; i < 2; ++i) {
for (int i = 0; i < 3; ++i) {
lbl_help[i] = new IconedLabel();
lbl_help[i]->setFrameShadow(QFrame::Plain);
lbl_help[i]->setFrameShape(QFrame::NoFrame);
lbl_help[i]->setDirection(IconedLabel::RightToLeft);
widget_help->layout()->addWidget(lbl_help[i]);
}
lbl_help[1]->setIcon(QIcon(":/icons/f1.png"));
lbl_help[1]->setText(tr("Press F1 for details"));
lbl_help[lhF1]->setIcon(QIcon(":/icons/f1.png"));
lbl_help[lhF1]->setText(tr("Press F1 for details"));
completer = new QCodeEditCompleter();
ui->textCode->setCursorWidth(qMax<int>(qRound(fontHeight() / 10.), 1));
ui->textCode->viewport()->setMouseTracking(true);
ui->textLines->viewport()->setAutoFillBackground(false);
ui->textLines->viewport()->setCursor(Qt::ArrowCursor);
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
@@ -246,6 +251,11 @@ QPlainTextEdit * QCodeEdit::textEdit() const {
}
void QCodeEdit::registerAutoCompletitionClass(int id, QCodeEdit::ACClassType ac_class, const QString & name, const QIcon & icon) {
ac_classes[id] = ACClass(id, ac_class, name, icon);
}
int QCodeEdit::skipRange(const QString & s, int pos, QChar oc, QChar cc, QChar sc) {
int cnt = 0;
bool skip = false;
@@ -400,6 +410,18 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
completer->hide();
hideHelp();
}
if (e->type() == QEvent::MouseMove) {
QMouseEvent * me = (QMouseEvent*)e;
if (me->modifiers().testFlag(Qt::ControlModifier)) {
showLink();
}
}
if (e->type() == QEvent::MouseButtonPress) {
QMouseEvent * me = (QMouseEvent*)e;
if (me->modifiers().testFlag(Qt::ControlModifier) && (me->button() == Qt::LeftButton)) {
gotoLink();
}
}
return QWidget::eventFilter(o, e);
}
if (o == ui->textCode) {
@@ -503,6 +525,9 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
return true;
}
break;
case Qt::Key_Control:
showLink();
break;
default: break;
}
if (!ke->text().isEmpty())
@@ -515,6 +540,9 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
QMetaObject::invokeMethod(this, "invokeAutoCompletition", Qt::QueuedConnection, Q_ARG(bool, false));
}
break;
case QEvent::KeyRelease:
hideLink();
break;
case QEvent::FocusOut:
if (_ignore_focus_out) {
_ignore_focus_out = false;
@@ -522,6 +550,7 @@ bool QCodeEdit::eventFilter(QObject * o, QEvent * e) {
}
case QEvent::Hide:
case QEvent::HideToParent:
hideLink();
case QEvent::MouseButtonPress:
//qDebug() << e;
completer->hide();
@@ -563,7 +592,7 @@ void QCodeEdit::changeEvent(QEvent * e) {
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
lbl_help[1]->setText(tr("Press F1 for details"));
lbl_help[lhF1]->setText(tr("Press F1 for details"));
break;
default:
break;
@@ -628,7 +657,7 @@ void QCodeEdit::highlightBrackets() {
void QCodeEdit::applyExtraSelection() {
ui->textCode->setExtraSelections(QList<QTextEdit::ExtraSelection>() << es_line << es_selected
<< es_custom << es_brackets << es_search_list << es_cursor);
<< es_custom << es_brackets << es_search_list << es_cursor << es_link);
}
@@ -953,6 +982,41 @@ void QCodeEdit::autoIndent() {
}
void QCodeEdit::showLink() {
QTextCursor tc = ui->textCode->cursorForPosition(ui->textCode->mapFromGlobal(QCursor::pos()));
tc.select(QTextCursor::WordUnderCursor);
link_entry = findEntryOnCursor(tc);
if (link_entry.isNull()) {
hideLink();
return;
}
if (!linkValidate(link_entry)) {
hideLink();
return;
}
es_link.cursor = tc;
ui->textCode->viewport()->setCursor(tc.isNull() ? Qt::IBeamCursor : Qt::PointingHandCursor);
applyExtraSelection();
//qDebug() << "showLink" << tc.selectedText() << link_entry.type << link_entry.name;
}
void QCodeEdit::hideLink() {
es_link.cursor = QTextCursor();
ui->textCode->viewport()->setCursor(Qt::IBeamCursor);
link_entry = ACEntry();
applyExtraSelection();
//qDebug() << "hideLink";
}
void QCodeEdit::gotoLink() {
if (link_entry.isNull()) return;
QMetaObject::invokeMethod(this, "_activateLink", Qt::QueuedConnection, Q_ARG(QCodeEdit::ACEntry, link_entry));
hideLink();
}
void QCodeEdit::scrollToTop() {
prev_lc = -1;
updateLines();
@@ -1036,20 +1100,20 @@ QString QCodeEdit::selectArg(QString s, int arg) {
}
void QCodeEdit::raiseHelp(QTextCursor tc, int arg) {
bool ok;
QPair<QStringList, QString> scope = getScope(tc, &ok);
QCodeEdit::ACEntry QCodeEdit::findEntryOnCursor(QTextCursor tc, int arg, ACClass * acc, QPair<QStringList, QString> * scope) {
bool ok = false;
QPair<QStringList, QString> sc = getScope(tc, &ok);
if (scope) *scope = sc;
QString st = tc.selectedText();
if (arg >= 0) st = scope.second;
if (arg >= 0) st = sc.second;
if (!ok || st.isEmpty()) {
hideHelp();
return;
return ACEntry();
}
ok = false;
ACList acl(autoCompletitionList(scope.first, scope.second));
foreach (const ACPair & i, acl) {
foreach (const StringsPair & s, i.second) {
QString ts = s.second;
ACList acl(autoCompletitionList(sc.first, sc.second));
foreach (const ACSection & i, acl) {
foreach (const ACEntry & s, i.second) {
QString ts = s.name;
//qDebug() << ts << st;
if (ts != st) {
if (ts.startsWith(st)) {
@@ -1063,31 +1127,43 @@ void QCodeEdit::raiseHelp(QTextCursor tc, int arg) {
continue;
}
//qDebug() << s.second << st;
ACClass acc = ac_classes.value(i.first);
lbl_help[0]->setIcon(acc.icon);
lbl_help[0]->setText(QString("<html><body>[%1] %2 %3</html></body>").arg(acc.name, s.first, selectArg(s.second, arg)));
ok = true;
break;
if (acc) *acc = ac_classes.value(i.first);
return s;
}
if (ok) break;
}
if (!ok) {
return ACEntry();
}
void QCodeEdit::raiseHelp(QTextCursor tc, int arg) {
ACClass acc;
QPair<QStringList, QString> scope;
ACEntry e = findEntryOnCursor(tc, arg, &acc, &scope);
if (e.isNull()) {
hideHelp();
return;
}
lbl_help[lhMain]->setText(QString("<html><body>[%1] %2 %3</html></body>").arg(acc.name, e.type, selectArg(e.name, arg)));
lbl_help[lhMain]->setIcon(acc.icon);
QString hint;
if (!e.hint.isEmpty())
hint = QString("<span style=\"font-style:italic;\">%1</span>").arg(e.hint);
lbl_help[lhHint]->setText(hint);
lbl_help[lhHint]->setHidden(hint.isEmpty());
//qDebug() << "help found" << tc.selectionStart() << tc.selectionEnd();
es_cursor.cursor = tc;
applyExtraSelection();
//tc.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
lbl_help[0]->setFont(font());
lbl_help[lhMain]->setFont(font());
qApp->processEvents();
widget_help->adjustSize();
widget_help->resize(widget_help->sizeHint());
qApp->processEvents();
QRect whr = ui->textCode->cursorRect(tc);
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
whr.setWidth(ui->textCode->fontMetrics().horizontalAdvance(st));
whr.setWidth(ui->textCode->fontMetrics().horizontalAdvance(tc.selectedText()));
#else
whr.setWidth(ui->textCode->fontMetrics().width(st));
whr.setWidth(ui->textCode->fontMetrics().width(tc.selectedText()));
#endif
QPoint whp;
whp.setX(whr.left() - whr.width() - (widget_help->width() - whr.width()) / 2);
@@ -1149,10 +1225,10 @@ QCodeEdit::ACList QCodeEdit::wordsCompletitionList(const QString & written) cons
acwl << stc.selectedText();
}
acwl.removeDuplicates();
ACPair acl;
ACSection acl;
acl.first = -1;
foreach (const QString & s, acwl)
acl.second << StringsPair("", s);
acl.second << ACEntry("", s);
ret << acl;
}
return ret;
@@ -1234,7 +1310,7 @@ void QCodeEdit::invokeAutoCompletition(bool force) {
if (word_complet) acl << wordsCompletitionList(scope.second);
QFont bf(font());
bf.setBold(true);
foreach (const ACPair & ac, acl) {
foreach (const ACSection & ac, acl) {
if (ac.second.isEmpty()) continue;
ACClass acc = ac_classes.value(ac.first);
completer->addItems(bf, acc, ac);