Core: Show 'Expression' completer under cursor position

This commit is contained in:
B0cho
2025-08-18 21:50:08 +02:00
parent 56be60c639
commit 8450fbae97
3 changed files with 55 additions and 1 deletions

View File

@@ -115,7 +115,7 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path,
}
else {
ui->expression->setMinimumWidth(300);
ui->expression->setMinimumHeight(50);
ui->expression->setMinimumHeight(80);
ui->msg->setWordWrap(true);
ui->msg->setMaximumHeight(200);
ui->msg->setMinimumWidth(280);

View File

@@ -888,6 +888,8 @@ void ExpressionCompleter::slotUpdate(const QString& prefix, int pos)
else if (auto itemView = popup()) {
itemView->setVisible(false);
}
Q_EMIT completerSlotUpdated();
}
ExpressionValidator::ExpressionValidator(QObject* parent)
@@ -1116,6 +1118,10 @@ void ExpressionTextEdit::setDocumentObject(const App::DocumentObject* currentDoc
&ExpressionTextEdit::textChanged2,
completer,
&ExpressionCompleter::slotUpdate);
connect(completer,
&ExpressionCompleter::completerSlotUpdated,
this,
&ExpressionTextEdit::adjustCompleterToCursor);
}
}
@@ -1135,6 +1141,7 @@ void ExpressionTextEdit::slotTextChanged()
{
if (!block) {
QTextCursor cursor = textCursor();
completer->popup()->setVisible(false); // hide the completer to avoid flickering
Q_EMIT textChanged2(cursor.block().text(), cursor.positionInBlock());
}
}
@@ -1185,6 +1192,49 @@ void ExpressionTextEdit::contextMenuEvent(QContextMenuEvent* event)
delete menu;
}
void ExpressionTextEdit::adjustCompleterToCursor()
{
if (!completer || !completer->popup()) {
return;
}
// get longest string width
int maxCompletionWidth = 0;
for (int i = 0; i < completer->completionModel()->rowCount(); ++i) {
const QModelIndex index = completer->completionModel()->index(i, 0);
const QString element = completer->completionModel()->data(index).toString();
maxCompletionWidth = std::max(maxCompletionWidth, static_cast<int>(element.size()) * completer->popup()->fontMetrics().averageCharWidth());
}
if (maxCompletionWidth == 0) {
return; // no completions available
}
const QPoint cursorPos = cursorRect(textCursor()).topLeft();
int posX = cursorPos.x();
int posY = cursorPos.y();
completer->popup()->setMaximumWidth(this->viewport()->width() * 0.6);
completer->popup()->setMaximumHeight(this->viewport()->height() * 0.6);
const QSize completerSize { maxCompletionWidth + 40, completer->popup()->size().height() }; // 40 is margin for scrollbar
completer->popup()->resize(completerSize);
// vertical correction
if (posY + completerSize.height() > viewport()->height()) {
posY -= completerSize.height();
} else {
posY += fontMetrics().height();
}
// horizontal correction
if (posX + completerSize.width() > viewport()->width()) {
posX = viewport()->width() - completerSize.width();
}
completer->popup()->move(mapToGlobal(QPoint{ posX, posY }));
completer->popup()->setVisible(true);
}
///////////////////////////////////////////////////////////////////////
ExpressionParameter* ExpressionParameter::instance()

View File

@@ -78,6 +78,9 @@ public:
public Q_SLOTS:
void slotUpdate(const QString &prefix, int pos);
Q_SIGNALS:
void completerSlotUpdated();
private:
void init();
QString pathFromIndex ( const QModelIndex & index ) const override;
@@ -139,6 +142,7 @@ Q_SIGNALS:
public Q_SLOTS:
void slotTextChanged();
void slotCompleteText(const QString & completionPrefix);
void adjustCompleterToCursor();
private:
ExpressionCompleter * completer;
bool block;