From a74cad92cb1932ada8c74ac7f410b398415c7443 Mon Sep 17 00:00:00 2001 From: vsv Date: Fri, 25 May 2018 15:42:27 +0300 Subject: [PATCH] Task 2.7: Code completion and parameters creation (not finished) --- src/ModuleBase/ModuleBase_ParamSpinBox.cpp | 338 +++++++++++------- src/ModuleBase/ModuleBase_ParamSpinBox.h | 93 +++-- .../ModuleBase_WidgetDoubleValue.cpp | 2 +- .../ModuleBase_WidgetExprEditor.cpp | 4 +- 4 files changed, 285 insertions(+), 152 deletions(-) diff --git a/src/ModuleBase/ModuleBase_ParamSpinBox.cpp b/src/ModuleBase/ModuleBase_ParamSpinBox.cpp index 37d888213..443868466 100644 --- a/src/ModuleBase/ModuleBase_ParamSpinBox.cpp +++ b/src/ModuleBase/ModuleBase_ParamSpinBox.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -36,20 +35,24 @@ #include #include +#include #include #include #include -//#define DEBUG_COMPLETE_WITH_PARAMETERS - ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrecision) - : ModuleBase_DoubleSpinBox(theParent, thePrecision), - myAcceptVariables(true) + : QAbstractSpinBox(theParent), + myPrecision(thePrecision), + myIsEquation(false), + myAcceptVariables(true), + myDecimals(3), + mySingleStep(1), + myMinimum(DBL_MIN), + myMaximum(DBL_MAX) { -#ifdef DEBUG_COMPLETE_WITH_PARAMETERS myCompleter = new QCompleter(this); - myCompleter->setWidget(this); + myCompleter->setWidget(lineEdit()); myCompleter->setCompletionMode(QCompleter::PopupCompletion); myCompleterModel = new QStringListModel(this); @@ -57,20 +60,24 @@ ModuleBase_ParamSpinBox::ModuleBase_ParamSpinBox(QWidget* theParent, int thePrec // Use sorted model to accelerate completion (QCompleter will use binary search) myCompleter->setModelSorting(QCompleter::CaseInsensitivelySortedModel); myCompleter->setCaseSensitivity(Qt::CaseInsensitive); + connect(myCompleter, SIGNAL(highlighted(const QString&)), + this, SLOT(insertCompletion(const QString&))); - lineEdit()->setCompleter(myCompleter); -#endif + // connectSignalsAndSlots(); + myEnabledBaseColor = palette().color(QPalette::Active, QPalette::Base); + connect(lineEdit(), SIGNAL(textChanged(const QString&)), + this, SLOT(onTextChanged(const QString&))); - connectSignalsAndSlots(); + myValidator = new QDoubleValidator(this); + myValidator->setLocale(locale()); + myValidator->setRange(myMinimum, myMaximum); } void ModuleBase_ParamSpinBox::setCompletionList(QStringList& theList) { -#ifdef DEBUG_COMPLETE_WITH_PARAMETERS theList.sort(); theList.removeDuplicates(); myCompleterModel->setStringList(theList); -#endif } /*! @@ -91,43 +98,53 @@ ModuleBase_ParamSpinBox::~ModuleBase_ParamSpinBox() */ void ModuleBase_ParamSpinBox::stepBy(int steps) { - if ((!myTextValue.isEmpty()) && hasVariable()) + if (hasVariable()) return; - ModuleBase_DoubleSpinBox::stepBy(steps); -} - -/*! - \brief Connect signals and slots. - */ -void ModuleBase_ParamSpinBox::connectSignalsAndSlots() -{ - connect(this, SIGNAL(valueChanged(const QString&)), - this, SLOT(onTextChanged(const QString&))); + double aVal = lineEdit()->text().toDouble(); + aVal += steps * mySingleStep; + setValue(aVal); + QAbstractSpinBox::stepBy(steps); } -void ModuleBase_ParamSpinBox::onTextChanged(const QString& text) +void ModuleBase_ParamSpinBox::onTextChanged(const QString& theText) { - myTextValue = text; - emit textChanged(text); + myIsEquation = hasVariable(theText); + emit textChanged(theText); } -double ModuleBase_ParamSpinBox::valueFromText(const QString& theText) const -{ - if (!hasVariable(theText)) - return ModuleBase_DoubleSpinBox::valueFromText(theText); - - // small hack: return hash of the string to initiate valuesChanged signal - return qHash(theText); -} -QString ModuleBase_ParamSpinBox::textFromValue (double theValue) const -{ - if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)){ - return myTextValue; - } - return ModuleBase_DoubleSpinBox::textFromValue(theValue); -} +///*! +// \brief Connect signals and slots. +// */ +//void ModuleBase_ParamSpinBox::connectSignalsAndSlots() +//{ +// connect(this, SIGNAL(valueChanged(const QString&)), +// this, SLOT(onTextChanged(const QString&))); +//} +// +//void ModuleBase_ParamSpinBox::onTextChanged(const QString& text) +//{ +// myTextValue = text; +// emit textChanged(text); +//} +// +//double ModuleBase_ParamSpinBox::valueFromText(const QString& theText) const +//{ +// if (!hasVariable(theText)) +// return ModuleBase_DoubleSpinBox::valueFromText(theText); +// +// // small hack: return hash of the string to initiate valuesChanged signal +// return qHash(theText); +//} +// +//QString ModuleBase_ParamSpinBox::textFromValue (double theValue) const +//{ +// if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)){ +// return myTextValue; +// } +// return ModuleBase_DoubleSpinBox::textFromValue(theValue); +//} /*! \brief This function is used to determine whether input is valid. @@ -139,13 +156,9 @@ QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) cons { // Trying to interpret the current input text as a numeric value if (!hasVariable(str)) - return ModuleBase_DoubleSpinBox::validate(str, pos); + return myValidator->validate(str, pos); - QValidator::State res = QValidator::Invalid; - if (isAcceptVariables()) { - res = QValidator::Acceptable; - } - return res; + return isAcceptVariables() ? QValidator::Acceptable : QValidator::Invalid; } /*! @@ -154,13 +167,25 @@ QValidator::State ModuleBase_ParamSpinBox::validate(QString& str, int& pos) cons The new value is ignored if the spinbox has a variable. */ -void ModuleBase_ParamSpinBox::setValue(const double value) +void ModuleBase_ParamSpinBox::setValue(double value) { - if (hasVariable()) - return; + myIsEquation = false; + double aVal = value; + if (aVal < myMinimum) + aVal = myMinimum; + else if (aVal > myMaximum) + aVal = myMaximum; + QString aText = QString::number(aVal, 'g', myDecimals); + lineEdit()->setText(aText); + emit textChanged(aText); +} - myTextValue = ModuleBase_DoubleSpinBox::textFromValue(value); - ModuleBase_DoubleSpinBox::setValue(value); +double ModuleBase_ParamSpinBox::value() const +{ + //if (myIsEquation) { + + //} + return lineEdit()->text().toDouble(); } /*! @@ -169,8 +194,11 @@ void ModuleBase_ParamSpinBox::setValue(const double value) */ void ModuleBase_ParamSpinBox::setText(const QString& value) { - myTextValue = value; - lineEdit()->setText(value); + myIsEquation = hasVariable(value); + if (myAcceptVariables && myIsEquation) { + lineEdit()->setText(value); + emit textChanged(value); + } } /*! @@ -181,6 +209,9 @@ void ModuleBase_ParamSpinBox::setText(const QString& value) void ModuleBase_ParamSpinBox::setAcceptVariables(const bool flag) { myAcceptVariables = flag; + if ((!myAcceptVariables) && myIsEquation) { + setValue(0); + } } /*! @@ -193,100 +224,161 @@ bool ModuleBase_ParamSpinBox::isAcceptVariables() const bool ModuleBase_ParamSpinBox::hasVariable() const { - if (myTextValue.isEmpty()) - return false; - return hasVariable(myTextValue); + return myIsEquation; } bool ModuleBase_ParamSpinBox::hasVariable(const QString& theText) const { - //const QString aDigitPattern = QString("[-+]?[0-9]*[%1]?[0-9]*([eE][-+]?[0-9]+)?"); - - //bool aHasDigit = false; - //{ - // QRegExp varNameMask(aDigitPattern.arg(".")); - // aHasDigit = varNameMask.exactMatch(theText); - //} - //if (!aHasDigit) - //{ - // QRegExp varNameMask(aDigitPattern.arg(",")); - // aHasDigit = varNameMask.exactMatch(theText); - //} bool isDouble = false; QLocale::c().toDouble(theText, &isDouble); - -// theText.toDouble(&isDouble); -// if (isDouble) { -// QLocale aLoc; // create default locale -// QChar aDecPnt = aLoc.decimalPoint(); -// if (aDecPnt == '.') -// isDouble = theText.contains(aDecPnt) || (!theText.contains(',')); -// else if (aDecPnt == ',') -// isDouble = theText.contains(aDecPnt) || (!theText.contains('.')); -// } return !isDouble; } +///*! +// \brief This function is used to determine whether input is valid. +// \return validating operation result +// */ +//ModuleBase_ParamSpinBox::State ModuleBase_ParamSpinBox::isValid(const QString& theText, +// double& theValue) const +//{ +// if (hasVariable() && !findVariable(theText, theValue)) { +// bool ok = false; +// theValue = locale().toDouble(theText, &ok); +// if (!ok) { +// return NoVariable; +// } +// } +// if (!checkRange(theValue)) { +// return Invalid; +// } +// +// return Acceptable; +//} +// +///*! +// \brief This function is used to check that string value lies within predefined range. +// \return check status +// */ +//bool ModuleBase_ParamSpinBox::checkRange(const double theValue) const +//{ +// return theValue >= minimum() && theValue <= maximum(); +//} +// +///*! +// \brief This function is used to determine whether input is a variable name and to get its value. +// \return status of search operation +// */ +//bool ModuleBase_ParamSpinBox::findVariable(const QString& theName, +// double& outValue) const +//{ +// ResultParameterPtr aParam; +// return ModelAPI_Tools::findVariable(FeaturePtr(), theName.toStdString(), outValue, aParam); +//} + /*! - \brief This function is used to determine whether input is valid. - \return validating operation result + \brief This function is called when the spinbox receives key press event. */ -ModuleBase_ParamSpinBox::State ModuleBase_ParamSpinBox::isValid(const QString& theText, - double& theValue) const +void ModuleBase_ParamSpinBox::keyReleaseEvent(QKeyEvent* e) { - if (hasVariable() && !findVariable(theText, theValue)) { - bool ok = false; - theValue = locale().toDouble(theText, &ok); - if (!ok) { - return NoVariable; + switch (e->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (myCompleter->popup()->isVisible()) { + myCompleter->popup()->hide(); + myIsEquation = true; } + emit textChanged(lineEdit()->text()); + return; + case Qt::Key_Space: + if (e->modifiers() & Qt::ControlModifier) { + myCompletePos = lineEdit()->cursorPosition(); + int aStart, aEnd; + QString aPrefix = getPrefix(aStart, aEnd); + myCompleter->setCompletionPrefix(aPrefix); + myCompleter->complete(); + } + break; + default: + QAbstractSpinBox::keyReleaseEvent(e); } - if (!checkRange(theValue)) { - return Invalid; - } - - return Acceptable; } -/*! - \brief This function is used to check that string value lies within predefined range. - \return check status - */ -bool ModuleBase_ParamSpinBox::checkRange(const double theValue) const +QString ModuleBase_ParamSpinBox::getPrefix(int& theStart, int& theEnd) const { - return theValue >= minimum() && theValue <= maximum(); + QString aPrefix; + QString aText = lineEdit()->text(); + theStart = theEnd = myCompletePos; + const int aLen = aText.length(); + if (aLen > 0) { + if (myCompletePos > 0) { + int aLastChar = myCompletePos - 1; + QChar aChar = aText.at(aLastChar); + while (aChar.isLetter() || aChar.isDigit()) { + aPrefix.prepend(aText.at(aLastChar)); + aLastChar--; + if (aLastChar < 0) + break; + aChar = aText.at(aLastChar); + } + theStart = aLastChar + 1; + } + if (myCompletePos < aLen) { + int aLastChar = myCompletePos; + QChar aChar = aText.at(aLastChar); + while (aChar.isLetter() || aChar.isDigit()) { + aPrefix.append(aText.at(aLastChar)); + aLastChar++; + if (aLastChar >= aLen) + break; + aChar = aText.at(aLastChar); + } + theEnd = aLastChar; + } + } + return aPrefix; } -/*! - \brief This function is used to determine whether input is a variable name and to get its value. - \return status of search operation - */ -bool ModuleBase_ParamSpinBox::findVariable(const QString& theName, - double& outValue) const + +void ModuleBase_ParamSpinBox::insertCompletion(const QString& theText) { - ResultParameterPtr aParam; - return ModelAPI_Tools::findVariable(FeaturePtr(), theName.toStdString(), outValue, aParam); + QString aText = lineEdit()->text(); + int aStart, aEnd; + QString aPrefix = getPrefix(aStart, aEnd); + + QString aResult; + int aPrefLen = aPrefix.length(); + if (aPrefLen == 0) + aResult = aText.insert(myCompletePos, theText); + else { + aResult = aText.left(aStart) + theText + aText.right(aText.length() - aEnd); + } + lineEdit()->setText(aResult); + myIsEquation = true; + + qDebug("### aPos=%i", myCompletePos); + qDebug("### text=%s", qPrintable(aText)); + qDebug("### prefix=%s", qPrintable(aPrefix)); + qDebug("### result=%s", qPrintable(aResult)); } -/*! - \brief This function is called when the spinbox receives key press event. - */ -//void ModuleBase_ParamSpinBox::keyPressEvent(QKeyEvent* e) + +///*! +// \brief This function is called when the spinbox receives show event. +// */ +//void ModuleBase_ParamSpinBox::showEvent(QShowEvent* theEvent) //{ -// if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { -// QWidget::keyPressEvent(e); -// } else { -// ModuleBase_DoubleSpinBox::keyPressEvent(e); +// ModuleBase_DoubleSpinBox::showEvent(theEvent); +// if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)) { +// setText(myTextValue); // } //} -/*! - \brief This function is called when the spinbox receives show event. - */ -void ModuleBase_ParamSpinBox::showEvent(QShowEvent* theEvent) +void ModuleBase_ParamSpinBox::setValueEnabled(bool theEnable) { - ModuleBase_DoubleSpinBox::showEvent(theEvent); - if ((!myTextValue.isEmpty()) && hasVariable(myTextValue)) { - setText(myTextValue); - } + setReadOnly(!theEnable); + + QPalette aPal = palette(); + aPal.setColor(QPalette::All, QPalette::Base, + theEnable ? myEnabledBaseColor : aPal.color(QPalette::Disabled, QPalette::Base)); + setPalette(aPal); } diff --git a/src/ModuleBase/ModuleBase_ParamSpinBox.h b/src/ModuleBase/ModuleBase_ParamSpinBox.h index a8cfe4407..16ab63789 100644 --- a/src/ModuleBase/ModuleBase_ParamSpinBox.h +++ b/src/ModuleBase/ModuleBase_ParamSpinBox.h @@ -23,9 +23,9 @@ #include "ModuleBase.h" -#include - +#include #include +#include class QStringListModel; class QCompleter; @@ -34,7 +34,7 @@ class QCompleter; * \ingroup GUI * An extension of a double spin box which let to use parameters and expressions for value definition */ -class MODULEBASE_EXPORT ModuleBase_ParamSpinBox : public ModuleBase_DoubleSpinBox +class MODULEBASE_EXPORT ModuleBase_ParamSpinBox : public QAbstractSpinBox { Q_OBJECT @@ -51,7 +51,7 @@ public: \param theParent a parent object \param thePrecision a precision of values display */ - explicit ModuleBase_ParamSpinBox( QWidget* theParent = 0, int thePrecision = -12 ); + ModuleBase_ParamSpinBox( QWidget* theParent = 0, int thePrecision = -12 ); /// Set list of completion strings void setCompletionList(QStringList&); @@ -60,15 +60,19 @@ public: virtual void stepBy(int); - virtual double valueFromText(const QString&) const; - virtual QString textFromValue (double value) const; +// virtual double valueFromText(const QString&) const; +// virtual QString textFromValue (double value) const; virtual QValidator::State validate(QString&, int&) const; virtual void setValue(double); + double value() const; + virtual void setText(const QString&); + QString text() const { return lineEdit()->text(); } + /// Set a flag about accepted variable void setAcceptVariables(const bool); @@ -78,42 +82,81 @@ public: /// Returns True if the input value contains variable bool hasVariable() const; + double minimum() const { return myMinimum; } + double maximum() const { return myMaximum; } + + void setMinimum(double theMin) { myMinimum = theMin; myValidator->setBottom(theMin); } + void setMaximum(double theMax) { myMaximum = theMax; myValidator->setTop(theMax); } + + int decimals() const { return myDecimals; } + void setDecimals(int thePrecision) { myDecimals = thePrecision; } + + double singleStep() const { return mySingleStep; } + void setSingleStep(double theStep) { mySingleStep = theStep; } + + void setValueEnabled(bool theEnable); + protected: + virtual void keyReleaseEvent(QKeyEvent *event); + + virtual StepEnabled stepEnabled() const { return StepUpEnabled | StepDownEnabled; } + /// Returns True if the given text contains variable /// \param theText a text string bool hasVariable(const QString& theText) const; - /// Returns state of the control - State isValid(const QString&, double&) const; - - /// Returns True if the given value is within min and max of the control - bool checkRange(const double) const; - - /// Finds a variable by its name. Returns true in success - /// \param theName a name of variable - /// \param outValue an output value of the variable - bool findVariable(const QString& theName, double& outValue) const; +// /// Returns state of the control +// State isValid(const QString&, double&) const; +// +// /// Returns True if the given value is within min and max of the control +// bool checkRange(const double) const; +// +// /// Finds a variable by its name. Returns true in success +// /// \param theName a name of variable +// /// \param outValue an output value of the variable +// bool findVariable(const QString& theName, double& outValue) const; signals: - void textChanged(const QString& theText); + void textChanged(const QString&); + +// protected: +// virtual void showEvent(QShowEvent*); +// +// protected slots: +// /// A slot called on text change +// void onTextChanged(const QString&); +// +// private: +// void connectSignalsAndSlots(); - protected: - virtual void showEvent(QShowEvent*); +private slots: + void insertCompletion(const QString&); - protected slots: - /// A slot called on text change void onTextChanged(const QString&); - private: - void connectSignalsAndSlots(); +private: + QString getPrefix(int& theStart, int& theEnd) const; - private: - QString myTextValue; + bool myIsEquation; bool myAcceptVariables; QStringListModel* myCompleterModel; QCompleter* myCompleter; + int myPrecision; + + double myMinimum; + double myMaximum; + + int myDecimals; + int myCompletePos; + + double mySingleStep; + + /// Cashed color of active base palette + QColor myEnabledBaseColor; + + QDoubleValidator* myValidator; }; #endif diff --git a/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp b/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp index 3b5b8b455..9a66c6b51 100644 --- a/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp +++ b/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp @@ -46,7 +46,7 @@ #include #endif -//#define DEBUG_COMPLETE_WITH_PARAMETERS +#define DEBUG_COMPLETE_WITH_PARAMETERS ModuleBase_WidgetDoubleValue::ModuleBase_WidgetDoubleValue(QWidget* theParent, const Config_WidgetAPI* theData) diff --git a/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp b/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp index 5da885e13..b90998825 100644 --- a/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp +++ b/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp @@ -103,9 +103,7 @@ void ExpressionEditor::performCompletion() QTextCursor aCursor = textCursor(); aCursor.select(QTextCursor::WordUnderCursor); const QString aPrefix = aCursor.selectedText(); - if (!aPrefix.isEmpty() && aPrefix.at(aPrefix.length() - 1).isLetter()) { - performCompletion(aPrefix); - } + performCompletion(aPrefix); } void ExpressionEditor::performCompletion(const QString& theCompletionPrefix) -- 2.39.2