From c158fd9cf6b8e1ed83806727637e706bd0f80edd Mon Sep 17 00:00:00 2001 From: B0cho Date: Wed, 16 Jul 2025 15:56:02 +0200 Subject: [PATCH 1/8] Core: Switch widget from 'ExpressionLineEdit' to 'ExpressionTextEdit' + minor dialog layout adjustments --- src/Gui/Dialogs/DlgExpressionInput.cpp | 27 +++++---------- src/Gui/Dialogs/DlgExpressionInput.h | 2 +- src/Gui/Dialogs/DlgExpressionInput.ui | 47 ++++++++++++++++++-------- src/Gui/ExpressionCompleter.cpp | 5 +++ src/Gui/ExpressionCompleter.h | 1 + 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/Gui/Dialogs/DlgExpressionInput.cpp b/src/Gui/Dialogs/DlgExpressionInput.cpp index 78c892f39b..a568f9d6d2 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.cpp +++ b/src/Gui/Dialogs/DlgExpressionInput.cpp @@ -79,22 +79,22 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path, initializeVarSets(); // Connect signal(s) - connect(ui->expression, &ExpressionLineEdit::textChanged, + connect(ui->expression, &ExpressionTextEdit::textChanged, this, &DlgExpressionInput::textChanged); connect(discardBtn, &QPushButton::clicked, this, &DlgExpressionInput::setDiscarded); if (expression) { - ui->expression->setText(QString::fromStdString(expression->toString())); + ui->expression->setPlainText(QString::fromStdString(expression->toString())); } else { QVariant text = parent->property("text"); if (text.canConvert()) { - ui->expression->setText(text.toString()); + ui->expression->setPlainText(text.toString()); } } - // Set document object on line edit to create auto completer + // Set document object on text edit to create auto completer DocumentObject * docObj = path.getDocumentObject(); ui->expression->setDocumentObject(docObj); @@ -114,7 +114,7 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path, setAttribute(Qt::WA_TranslucentBackground, true); } else { - ui->expression->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + ui->expression->setMinimumHeight(50); ui->horizontalSpacer_3->changeSize(0, 2); ui->verticalLayout->setContentsMargins(9, 9, 9, 9); this->adjustSize(); @@ -360,8 +360,10 @@ void DlgExpressionInput::checkExpression(const QString& text) static const bool NoCheckExpr = false; -void DlgExpressionInput::textChanged(const QString &text) +void DlgExpressionInput::textChanged() { + const QString& text = ui->expression->toPlainText(); + if (text.isEmpty()) { okBtn->setDisabled(true); discardBtn->setDefault(true); @@ -371,17 +373,6 @@ void DlgExpressionInput::textChanged(const QString &text) okBtn->setDefault(true); try { - //resize the input field according to text size - QFontMetrics fm(ui->expression->font()); - int width = QtTools::horizontalAdvance(fm, text) + 15; - if (width < minimumWidth) - ui->expression->setMinimumWidth(minimumWidth); - else - ui->expression->setMinimumWidth(width); - - if(this->width() < ui->expression->minimumWidth()) - setMinimumWidth(ui->expression->minimumWidth()); - checkExpression(text); if (varSetsVisible) { // If varsets are visible, check whether the varset info also @@ -917,7 +908,7 @@ void DlgExpressionInput::updateVarSetInfo(bool checkExpr) if (checkExpr) { // We have to check the text of the expression as well try { - checkExpression(ui->expression->text()); + checkExpression(ui->expression->toPlainText()); } catch (Base::Exception&) { okBtn->setEnabled(false); diff --git a/src/Gui/Dialogs/DlgExpressionInput.h b/src/Gui/Dialogs/DlgExpressionInput.h index ce62aa2d95..90b6c1760a 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.h +++ b/src/Gui/Dialogs/DlgExpressionInput.h @@ -111,7 +111,7 @@ private: QString& message) const; private Q_SLOTS: - void textChanged(const QString & text); + void textChanged(); void setDiscarded(); void onCheckVarSets(int state); void onVarSetSelected(int index); diff --git a/src/Gui/Dialogs/DlgExpressionInput.ui b/src/Gui/Dialogs/DlgExpressionInput.ui index 8736c83604..3d5dc2f374 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.ui +++ b/src/Gui/Dialogs/DlgExpressionInput.ui @@ -143,9 +143,9 @@ - + - + 0 0 @@ -312,24 +312,41 @@ - - - - 0 - 0 - - - - QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::Reset - - + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + 0 + 0 + + + + QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::Reset + + + + - Gui::ExpressionLineEdit - QLineEdit + Gui::ExpressionTextEdit + QTextEdit
ExpressionCompleter.h
diff --git a/src/Gui/ExpressionCompleter.cpp b/src/Gui/ExpressionCompleter.cpp index 6f3dc29f50..ee206c8ff9 100644 --- a/src/Gui/ExpressionCompleter.cpp +++ b/src/Gui/ExpressionCompleter.cpp @@ -1085,6 +1085,11 @@ void ExpressionTextEdit::setExactMatch(bool enabled) } } +QSize ExpressionTextEdit::sizeHint() const +{ + return QSize(200, 30); +} + void ExpressionTextEdit::setDocumentObject(const App::DocumentObject* currentDocObj) { if (completer) { diff --git a/src/Gui/ExpressionCompleter.h b/src/Gui/ExpressionCompleter.h index cf7f5f20c9..af29fe4396 100644 --- a/src/Gui/ExpressionCompleter.h +++ b/src/Gui/ExpressionCompleter.h @@ -130,6 +130,7 @@ public: bool completerActive() const; void hideCompleter(); void setExactMatch(bool enabled=true); + QSize sizeHint() const override; protected: void keyPressEvent(QKeyEvent * event) override; void contextMenuEvent(QContextMenuEvent * event) override; From 05517d2ff45fd169eaecc866dfd064f381e77963 Mon Sep 17 00:00:00 2001 From: B0cho Date: Mon, 4 Aug 2025 22:13:17 +0200 Subject: [PATCH 2/8] Core: Fix 'Result' message text not wrapped in Expression editor --- src/Gui/Dialogs/DlgExpressionInput.cpp | 66 +++++++++++++++++++------- src/Gui/Dialogs/DlgExpressionInput.h | 5 +- src/Gui/Dialogs/DlgExpressionInput.ui | 21 ++++++-- src/Gui/QuantitySpinBox.cpp | 1 - src/Gui/SpinBox.cpp | 1 - src/Gui/Widgets.cpp | 1 - 6 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/Gui/Dialogs/DlgExpressionInput.cpp b/src/Gui/Dialogs/DlgExpressionInput.cpp index a568f9d6d2..ebc83d90d8 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.cpp +++ b/src/Gui/Dialogs/DlgExpressionInput.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include "Dialogs/DlgExpressionInput.h" #include "ui_DlgExpressionInput.h" @@ -64,7 +65,6 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path, , path(_path) , discarded(false) , impliedUnit(_impliedUnit) - , minimumWidth(10) , varSetsVisible(false) , comboBoxGroup(this) { @@ -114,7 +114,11 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path, setAttribute(Qt::WA_TranslucentBackground, true); } else { + ui->expression->setMinimumWidth(300); ui->expression->setMinimumHeight(50); + ui->msg->setWordWrap(true); + ui->msg->setMaximumHeight(200); + ui->msg->setMinimumWidth(280); ui->horizontalSpacer_3->changeSize(0, 2); ui->verticalLayout->setContentsMargins(9, 9, 9, 9); this->adjustSize(); @@ -348,12 +352,12 @@ void DlgExpressionInput::checkExpression(const QString& text) } numberRange.throwIfOutOfRange(value); - - ui->msg->setText(msg); + message = msg.toStdString(); } else { - ui->msg->setText(QString::fromStdString(result->toString())); + message = result->toString(); } + setMsgText(); } } @@ -382,7 +386,8 @@ void DlgExpressionInput::textChanged() } } catch (Base::Exception & e) { - ui->msg->setText(QString::fromUtf8(e.what())); + message = e.what(); + setMsgText(); QPalette p(ui->msg->palette()); p.setColor(QPalette::WindowText, Qt::red); ui->msg->setPalette(p); @@ -396,19 +401,6 @@ void DlgExpressionInput::setDiscarded() reject(); } -void DlgExpressionInput::setExpressionInputSize(int width, int height) -{ - if (ui->expression->minimumHeight() < height) { - ui->expression->setMinimumHeight(height); - } - - if (ui->expression->minimumWidth() < width) { - ui->expression->setMinimumWidth(width); - } - - minimumWidth = width; -} - void DlgExpressionInput::mouseReleaseEvent(QMouseEvent* event) { Q_UNUSED(event); @@ -926,4 +918,42 @@ bool DlgExpressionInput::needReportOnVarSet() return reportGroup(comboBoxGroup.currentText()) || reportName(); } +void DlgExpressionInput::resizeEvent(QResizeEvent *event) +{ + // When the dialog is resized, message text may need to be re-wrapped + if (!this->message.empty() && event->size() != event->oldSize()) { + setMsgText(); + } + QDialog::resizeEvent(event); +} + +void DlgExpressionInput::setMsgText() +{ + if (!this->message.size()) { + return; + } + + const QFontMetrics msgFontMetrics{ ui->msg->font() }; + + // find words longer than length of msg widget + // then insert newline to wrap it + std::string wrappedMsg{}; + static constexpr int msgContentMargins = 50; + const int maxWordLength = (ui->msg->width() - msgContentMargins) / msgFontMetrics.averageCharWidth(); + + const auto wrappableWordPattern = std::regex{ "\\S{" + std::to_string(maxWordLength) + "}" }; + auto it = std::sregex_iterator{ this->message.cbegin(), this->message.cend(), wrappableWordPattern }; + const auto itEnd = std::sregex_iterator{}; + + int lastPos = 0; + for (; it != itEnd; ++it) { + wrappedMsg += this->message.substr(lastPos, it->position() - lastPos); + wrappedMsg += it->str() + "\n"; + lastPos = it->position() + it->length(); + } + wrappedMsg += this->message.substr(lastPos); + + ui->msg->setText(QString::fromStdString(wrappedMsg)); +} + #include "moc_DlgExpressionInput.cpp" diff --git a/src/Gui/Dialogs/DlgExpressionInput.h b/src/Gui/Dialogs/DlgExpressionInput.h index 90b6c1760a..0bc88fdda9 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.h +++ b/src/Gui/Dialogs/DlgExpressionInput.h @@ -77,7 +77,6 @@ public: bool discardedFormula() const { return discarded; } QPoint expressionPosition() const; - void setExpressionInputSize(int width, int height); public Q_SLOTS: void show(); @@ -86,6 +85,7 @@ public Q_SLOTS: protected: void mouseReleaseEvent(QMouseEvent* event) override; void mousePressEvent(QMouseEvent* event) override; + void resizeEvent(QResizeEvent* event) override; private: Base::Type getTypePath(); @@ -109,6 +109,7 @@ private: const App::DocumentObject* obj, QString& message) const; bool isGroupNameValid(const QString& nameGroup, QString& message) const; + void setMsgText(); private Q_SLOTS: void textChanged(); @@ -127,7 +128,7 @@ private: const Base::Unit impliedUnit; NumberRange numberRange; - int minimumWidth; + std::string message; bool varSetsVisible; QPushButton* okBtn = nullptr; diff --git a/src/Gui/Dialogs/DlgExpressionInput.ui b/src/Gui/Dialogs/DlgExpressionInput.ui index 3d5dc2f374..3e21e2a85d 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.ui +++ b/src/Gui/Dialogs/DlgExpressionInput.ui @@ -18,7 +18,7 @@ - 300 + 350 0 @@ -48,7 +48,7 @@ - + 0 0 @@ -71,6 +71,12 @@ + + + 0 + 0 + + Result @@ -78,6 +84,12 @@ + + + 0 + 0 + + @@ -130,6 +142,9 @@ Qt::Orientation::Horizontal + + QSizePolicy::Policy::Fixed + 0 @@ -145,7 +160,7 @@ - + 0 0 diff --git a/src/Gui/QuantitySpinBox.cpp b/src/Gui/QuantitySpinBox.cpp index b7febd34e6..9f380120d8 100644 --- a/src/Gui/QuantitySpinBox.cpp +++ b/src/Gui/QuantitySpinBox.cpp @@ -618,7 +618,6 @@ void QuantitySpinBox::openFormulaDialog() QPoint pos = mapToGlobal(QPoint(0,0)); box->move(pos-box->expressionPosition()); - box->setExpressionInputSize(width(), height()); Gui::adjustDialogPosition(box); Q_EMIT showFormulaDialog(true); diff --git a/src/Gui/SpinBox.cpp b/src/Gui/SpinBox.cpp index 89287b4f54..2fd840fecc 100644 --- a/src/Gui/SpinBox.cpp +++ b/src/Gui/SpinBox.cpp @@ -206,7 +206,6 @@ void ExpressionSpinBox::openFormulaDialog() QPoint pos = spinbox->mapToGlobal(QPoint(0,0)); box->move(pos-box->expressionPosition()); - box->setExpressionInputSize(spinbox->width(), spinbox->height()); Gui::adjustDialogPosition(box); } diff --git a/src/Gui/Widgets.cpp b/src/Gui/Widgets.cpp index 17d1b1a397..a4d1f1505e 100644 --- a/src/Gui/Widgets.cpp +++ b/src/Gui/Widgets.cpp @@ -1606,7 +1606,6 @@ void ExpLineEdit::openFormulaDialog() QPoint pos = mapToGlobal(QPoint(0,0)); box->move(pos-box->expressionPosition()); - box->setExpressionInputSize(width(), height()); Gui::adjustDialogPosition(box); } From 26ea2a9bb2cd5cc27cb3d37ddffb1a6551769f4c Mon Sep 17 00:00:00 2001 From: B0cho Date: Wed, 6 Aug 2025 17:05:18 +0200 Subject: [PATCH 3/8] Core: Remove redundant spacers from Expression editor --- src/Gui/Dialogs/DlgExpressionInput.cpp | 1 - src/Gui/Dialogs/DlgExpressionInput.ui | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/src/Gui/Dialogs/DlgExpressionInput.cpp b/src/Gui/Dialogs/DlgExpressionInput.cpp index ebc83d90d8..0ea0049805 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.cpp +++ b/src/Gui/Dialogs/DlgExpressionInput.cpp @@ -119,7 +119,6 @@ DlgExpressionInput::DlgExpressionInput(const App::ObjectIdentifier & _path, ui->msg->setWordWrap(true); ui->msg->setMaximumHeight(200); ui->msg->setMinimumWidth(280); - ui->horizontalSpacer_3->changeSize(0, 2); ui->verticalLayout->setContentsMargins(9, 9, 9, 9); this->adjustSize(); // It is strange that (at least on Linux) DlgExpressionInput will shrink diff --git a/src/Gui/Dialogs/DlgExpressionInput.ui b/src/Gui/Dialogs/DlgExpressionInput.ui index 3e21e2a85d..0bc6a96f6c 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.ui +++ b/src/Gui/Dialogs/DlgExpressionInput.ui @@ -173,19 +173,6 @@ - - - - Qt::Orientation::Horizontal - - - - 0 - 2 - - - - From d6ea05de75c0505ef7f70dbfb5be4b81e45f495a Mon Sep 17 00:00:00 2001 From: B0cho Date: Thu, 7 Aug 2025 12:24:24 +0200 Subject: [PATCH 4/8] Core: Make too long 'Result' message elide in Expression editor --- src/Gui/Dialogs/DlgExpressionInput.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Gui/Dialogs/DlgExpressionInput.cpp b/src/Gui/Dialogs/DlgExpressionInput.cpp index 0ea0049805..4bb9d22371 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.cpp +++ b/src/Gui/Dialogs/DlgExpressionInput.cpp @@ -937,8 +937,8 @@ void DlgExpressionInput::setMsgText() // find words longer than length of msg widget // then insert newline to wrap it std::string wrappedMsg{}; - static constexpr int msgContentMargins = 50; - const int maxWordLength = (ui->msg->width() - msgContentMargins) / msgFontMetrics.averageCharWidth(); + const int msgContentWidth = ui->msg->width() * 0.85; // 0.85 is a magic number for some padding + const int maxWordLength = msgContentWidth / msgFontMetrics.averageCharWidth(); const auto wrappableWordPattern = std::regex{ "\\S{" + std::to_string(maxWordLength) + "}" }; auto it = std::sregex_iterator{ this->message.cbegin(), this->message.cend(), wrappableWordPattern }; @@ -953,6 +953,14 @@ void DlgExpressionInput::setMsgText() wrappedMsg += this->message.substr(lastPos); ui->msg->setText(QString::fromStdString(wrappedMsg)); + + // elide text if it is going out of widget bounds + // note: this is only 'rough elide', as this text is usually not very long; + const int msgLinesLimit = 3; + if (wrappedMsg.size() > msgContentWidth / msgFontMetrics.averageCharWidth() * msgLinesLimit) { + const QString elidedMsg = msgFontMetrics.elidedText(QString::fromStdString(wrappedMsg), Qt::ElideRight, msgContentWidth * msgLinesLimit); + ui->msg->setText(elidedMsg); + } } #include "moc_DlgExpressionInput.cpp" From ab11ede792ce67516f334104b8d786d486b3acfe Mon Sep 17 00:00:00 2001 From: B0cho Date: Mon, 18 Aug 2025 11:50:36 +0200 Subject: [PATCH 5/8] Core: Fix 'QTextEdit' background-color Suggested and Co-authored-by: @MisterMakerNL --- src/Gui/Stylesheets/FreeCAD.qss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Gui/Stylesheets/FreeCAD.qss b/src/Gui/Stylesheets/FreeCAD.qss index 8783b77067..05aab36e2c 100644 --- a/src/Gui/Stylesheets/FreeCAD.qss +++ b/src/Gui/Stylesheets/FreeCAD.qss @@ -804,7 +804,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-specific-widgets report view --------------------------------------------------------------------------- */ QTextEdit { - background-color: @PrimaryColor; + background-color: @TextEditFieldBackgroundColor; border-top: 1px solid @GeneralBorderColor; } From ae8679b8f643413c2822da889a945367b38f9c86 Mon Sep 17 00:00:00 2001 From: B0cho Date: Mon, 18 Aug 2025 21:50:08 +0200 Subject: [PATCH 6/8] Core: Show 'Expression' completer under cursor position --- src/Gui/Dialogs/DlgExpressionInput.cpp | 2 +- src/Gui/ExpressionCompleter.cpp | 50 ++++++++++++++++++++++++++ src/Gui/ExpressionCompleter.h | 4 +++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/Gui/Dialogs/DlgExpressionInput.cpp b/src/Gui/Dialogs/DlgExpressionInput.cpp index 4bb9d22371..7e4ec4a6dd 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.cpp +++ b/src/Gui/Dialogs/DlgExpressionInput.cpp @@ -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); diff --git a/src/Gui/ExpressionCompleter.cpp b/src/Gui/ExpressionCompleter.cpp index ee206c8ff9..15dff8d3bf 100644 --- a/src/Gui/ExpressionCompleter.cpp +++ b/src/Gui/ExpressionCompleter.cpp @@ -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(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() diff --git a/src/Gui/ExpressionCompleter.h b/src/Gui/ExpressionCompleter.h index af29fe4396..fce6efa163 100644 --- a/src/Gui/ExpressionCompleter.h +++ b/src/Gui/ExpressionCompleter.h @@ -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; From a3c65046f9f6704cc8c5926199c6f90884fda57e Mon Sep 17 00:00:00 2001 From: B0cho Date: Tue, 19 Aug 2025 14:06:49 +0200 Subject: [PATCH 7/8] Core: Move 'Result' message below 'Expression' input --- src/Gui/Dialogs/DlgExpressionInput.ui | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Gui/Dialogs/DlgExpressionInput.ui b/src/Gui/Dialogs/DlgExpressionInput.ui index 0bc6a96f6c..83a8f077b2 100644 --- a/src/Gui/Dialogs/DlgExpressionInput.ui +++ b/src/Gui/Dialogs/DlgExpressionInput.ui @@ -45,6 +45,26 @@ 0 + + + + + + + 0 + 0 + + + + + 10 + 10 + + + + + + @@ -155,26 +175,6 @@ - - - - - - - 0 - 0 - - - - - 10 - 10 - - - - - - From 3f24b767d2cb323466a478b0bf29ec9dd6302e8d Mon Sep 17 00:00:00 2001 From: B0cho Date: Fri, 5 Sep 2025 21:12:49 +0200 Subject: [PATCH 8/8] Core: Fix 'Expression editor' handling with keyboard --- src/Gui/ExpressionCompleter.cpp | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/Gui/ExpressionCompleter.cpp b/src/Gui/ExpressionCompleter.cpp index 15dff8d3bf..fc8058f0ef 100644 --- a/src/Gui/ExpressionCompleter.cpp +++ b/src/Gui/ExpressionCompleter.cpp @@ -1164,6 +1164,57 @@ void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix) void ExpressionTextEdit::keyPressEvent(QKeyEvent* e) { Base::FlagToggler flag(block, true); + + // Shift+Enter - insert a new line + if ((e->modifiers() & Qt::ShiftModifier) && (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) { + this->setPlainText(this->toPlainText() + QLatin1Char('\n')); + this->moveCursor(QTextCursor::End); + if (completer) { + completer->popup()->setVisible(false); + } + e->accept(); + return; + } + + // handling if completer is visible + if (completer && completer->popup()->isVisible()) { + switch (e->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Escape: + case Qt::Key_Backtab: + // default action + e->ignore(); + return; + + case Qt::Key_Tab: + // if no completion is selected, take top one + if (!completer->popup()->currentIndex().isValid()) { + completer->popup()->setCurrentIndex(completer->popup()->model()->index(0, 0)); + } + // insert completion + completer->setCurrentRow(completer->popup()->currentIndex().row()); + slotCompleteText(completer->currentCompletion()); + + // refresh completion list + completer->setCompletionPrefix(completer->currentCompletion()); + if (completer->completionCount() == 1) { + completer->popup()->setVisible(false); + } + e->accept(); + return; + + default: + break; + } + } + + // Enter, Return or Tab - request default action + if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return || e->key() == Qt::Key_Tab) { + e->ignore(); + return; + } + QPlainTextEdit::keyPressEvent(e); }