From dcf404632ceef566c6785bf95f9eee1b6159d32b Mon Sep 17 00:00:00 2001 From: sbh Date: Thu, 2 Apr 2015 12:27:59 +0300 Subject: [PATCH] Expression editor implementation --- src/Config/Config_Keywords.h | 1 + src/ModelAPI/ModelAPI_ResultParameters.h | 4 +- src/ModuleBase/CMakeLists.txt | 2 + .../ModuleBase_WidgetExprEditor.cpp | 207 ++++++++++++++++++ src/ModuleBase/ModuleBase_WidgetExprEditor.h | 85 +++++++ src/ModuleBase/ModuleBase_WidgetFactory.cpp | 40 ++-- src/ModuleBase/ModuleBase_WidgetLineEdit.cpp | 9 +- src/ParametersPlugin/plugin-Parameters.xml | 4 +- src/XGUI/pictures/expression.png | Bin 375 -> 297 bytes 9 files changed, 321 insertions(+), 31 deletions(-) create mode 100644 src/ModuleBase/ModuleBase_WidgetExprEditor.cpp create mode 100644 src/ModuleBase/ModuleBase_WidgetExprEditor.h diff --git a/src/Config/Config_Keywords.h b/src/Config/Config_Keywords.h index bc115a764..a9f44bf63 100644 --- a/src/Config/Config_Keywords.h +++ b/src/Config/Config_Keywords.h @@ -31,6 +31,7 @@ const static char* WDG_SHAPE_SELECTOR = "shape_selector"; const static char* WDG_CHOICE = "choice"; const static char* WDG_DOUBLEVALUE_EDITOR = "doublevalue_editor"; const static char* WDG_FILE_SELECTOR= "file_selector"; +const static char* WDG_EXPR_EDITOR = "expr_editor"; // Containers const static char* WDG_GROUP = "groupbox"; const static char* WDG_CHECK_GROUP = "check_groupbox"; diff --git a/src/ModelAPI/ModelAPI_ResultParameters.h b/src/ModelAPI/ModelAPI_ResultParameters.h index 3b8bd1535..aae2a15ed 100644 --- a/src/ModelAPI/ModelAPI_ResultParameters.h +++ b/src/ModelAPI/ModelAPI_ResultParameters.h @@ -4,8 +4,8 @@ // Created: 07 Jul 2014 // Author: Vitaly SMETANNIKOV -#ifndef ModelAPI_ResultParameters_H_ -#define ModelAPI_ResultParameters_H_ +#ifndef MODELAPI_RESULTPARAMETERS_H_ +#define MODELAPI_RESULTPARAMETERS_H_ #include "ModelAPI_Result.h" diff --git a/src/ModuleBase/CMakeLists.txt b/src/ModuleBase/CMakeLists.txt index b623713fc..3f5ba997f 100644 --- a/src/ModuleBase/CMakeLists.txt +++ b/src/ModuleBase/CMakeLists.txt @@ -51,6 +51,7 @@ SET(PROJECT_HEADERS ModuleBase_WidgetSwitch.h ModuleBase_WidgetToolbox.h ModuleBase_WidgetValidated.h + ModuleBase_WidgetExprEditor.h ) SET(PROJECT_SOURCES @@ -92,6 +93,7 @@ SET(PROJECT_SOURCES ModuleBase_WidgetSwitch.cpp ModuleBase_WidgetToolbox.cpp ModuleBase_WidgetValidated.cpp + ModuleBase_WidgetExprEditor.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp b/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp new file mode 100644 index 000000000..64ca2e312 --- /dev/null +++ b/src/ModuleBase/ModuleBase_WidgetExprEditor.cpp @@ -0,0 +1,207 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D + +/* + * ModuleBase_WidgetExprEditor.cpp + * + * Created on: Aug 28, 2014 + * Author: sbh + */ + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +ExpressionEditor::ExpressionEditor(QWidget* theParent) +: QPlainTextEdit(theParent) +{ + myCompleter = new QCompleter(this); + myCompleter->setWidget(this); + myCompleter->setCompletionMode(QCompleter::PopupCompletion); + + myCompleterModel = new QStringListModel(this); + myCompleter->setModel(myCompleterModel); + // Use sorted model to accelerate completion (QCompleter will use binary search) + myCompleter->setModelSorting(QCompleter::CaseInsensitivelySortedModel); + myCompleter->setCaseSensitivity(Qt::CaseInsensitive); + + connect(myCompleter, SIGNAL(activated(const QString&)), + this, SLOT(insertCompletion(const QString&))); + (void) new QShortcut(QKeySequence(tr("Ctrl+Space", "Complete")), + this, SLOT(performCompletion())); +} + +ExpressionEditor::~ExpressionEditor() +{ + +} + +void ExpressionEditor::setCompletionList(QStringList& theList) +{ + theList.sort(); + theList.removeDuplicates(); + myCompleterModel->setStringList(theList); +} + +void ExpressionEditor::insertCompletion(const QString& theCompletion, bool isSingleWord) +{ + QTextCursor aCursor = textCursor(); + int numberOfCharsToComplete = theCompletion.length() - + myCompleter->completionPrefix().length(); + int insertionPosition = aCursor.position(); + aCursor.insertText(theCompletion.right(numberOfCharsToComplete)); + if (isSingleWord) { + aCursor.setPosition(insertionPosition); + aCursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + myCompletedAndSelected = true; + } + setTextCursor(aCursor); +} + +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); + } +} + +void ExpressionEditor::performCompletion(const QString& theCompletionPrefix) +{ + //populate model? + if (theCompletionPrefix != myCompleter->completionPrefix()) { + myCompleter->setCompletionPrefix(theCompletionPrefix); + myCompleter->popup()->setCurrentIndex(myCompleter->completionModel()->index(0, 0)); + } + if (myCompleter->completionCount() == 1) { + insertCompletion(myCompleter->currentCompletion(), true); + } else { + QRect aRect = cursorRect(); + aRect.setWidth(myCompleter->popup()->sizeHintForColumn(0) + + myCompleter->popup()->verticalScrollBar()->sizeHint().width()); + myCompleter->complete(aRect); + } +} + +void ExpressionEditor::keyPressEvent(QKeyEvent* theEvent) +{ + if (myCompletedAndSelected && handledCompletedAndSelected(theEvent)) + return; + myCompletedAndSelected = false; + if (myCompleter->popup()->isVisible()) { + switch (theEvent->key()) { + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Escape: + theEvent->ignore(); + return; + default: + myCompleter->popup()->hide(); + break; + } + } + QPlainTextEdit::keyPressEvent(theEvent); +} + +bool ExpressionEditor::handledCompletedAndSelected(QKeyEvent* theEvent) +{ + myCompletedAndSelected = false; + QTextCursor aCursor = textCursor(); + switch (theEvent->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: aCursor.clearSelection(); break; + case Qt::Key_Escape: aCursor.removeSelectedText(); break; + default: return false; + } + setTextCursor(aCursor); + theEvent->accept(); + return true; +} + +ModuleBase_WidgetExprEditor::ModuleBase_WidgetExprEditor(QWidget* theParent, + const Config_WidgetAPI* theData, + const std::string& theParentId) + : ModuleBase_ModelWidget(theParent, theData, theParentId) +{ + QVBoxLayout* aMainLay = new QVBoxLayout(this); + ModuleBase_Tools::adjustMargins(aMainLay); + + myEditor = new ExpressionEditor(this); + myEditor->setMinimumHeight(20); + aMainLay->addWidget(myEditor); + this->setLayout(aMainLay); + + connect(myEditor, SIGNAL(textChanged()), this, SLOT(onTextChanged())); +} + +ModuleBase_WidgetExprEditor::~ModuleBase_WidgetExprEditor() +{ +} + +bool ModuleBase_WidgetExprEditor::storeValueCustom() const +{ + // A rare case when plugin was not loaded. + if(!myFeature) + return false; + DataPtr aData = myFeature->data(); + AttributeStringPtr aStringAttr = aData->string(attributeID()); + QString aWidgetValue = myEditor->toPlainText(); + aStringAttr->setValue(aWidgetValue.toStdString()); + updateObject(myFeature); + return true; +} + +bool ModuleBase_WidgetExprEditor::restoreValue() +{ + // A rare case when plugin was not loaded. + if(!myFeature) + return false; + DataPtr aData = myFeature->data(); + AttributeStringPtr aStringAttr = aData->string(attributeID()); + + bool isBlocked = myEditor->blockSignals(true); + QTextCursor aCursor = myEditor->textCursor(); + int pos = aCursor.position(); + std::string aRestoredStr = aStringAttr->value(); + myEditor->setPlainText(QString::fromStdString(aRestoredStr)); + aCursor.setPosition(pos); + myEditor->setTextCursor(aCursor); + myEditor->blockSignals(isBlocked); + + return true; +} + +QList ModuleBase_WidgetExprEditor::getControls() const +{ + QList result; + result << myEditor; + return result; +} + +void ModuleBase_WidgetExprEditor::onTextChanged() +{ + storeValue(); +} diff --git a/src/ModuleBase/ModuleBase_WidgetExprEditor.h b/src/ModuleBase/ModuleBase_WidgetExprEditor.h new file mode 100644 index 000000000..41a209f20 --- /dev/null +++ b/src/ModuleBase/ModuleBase_WidgetExprEditor.h @@ -0,0 +1,85 @@ +// Copyright (C) 2014-20xx CEA/DEN, EDF R&D + +/* + * ModuleBase_WidgetExprEditor.h + * + * Created on: Oct 8, 2014 + * Author: sbh + */ + +#ifndef MODULEBASE_WIDGETEXPREDITOR_H_ +#define MODULEBASE_WIDGETEXPREDITOR_H_ + +#include +#include + +#include +#include +#include +#include + +class QWidget; +class QStringListModel; +class QCompleter; + +class ExpressionEditor: public QPlainTextEdit +{ + Q_OBJECT + + public: + explicit ExpressionEditor(QWidget* theParent = 0); + virtual ~ExpressionEditor(); + + void setCompletionList(QStringList&); + + public slots: + void insertCompletion(const QString&, bool isSingleWord = false); + void performCompletion(); + + protected: + void performCompletion(const QString& theCompletionPrefix); + virtual void keyPressEvent(QKeyEvent* theEvent); + bool handledCompletedAndSelected(QKeyEvent* theEvent); + + private: + QStringListModel* myCompleterModel; + QCompleter* myCompleter; + bool myCompletedAndSelected; +}; + +/** +* \ingroup GUI +* TODO(sbh) add doc +*/ +class MODULEBASE_EXPORT ModuleBase_WidgetExprEditor : public ModuleBase_ModelWidget +{ + Q_OBJECT + public: + /// Constructor + /// \param theParent the parent object + /// \param theData the widget configuration. + /// \param theParentId is Id of a parent of the current attribute + ModuleBase_WidgetExprEditor(QWidget* theParent, + const Config_WidgetAPI* theData, + const std::string& theParentId); + virtual ~ModuleBase_WidgetExprEditor(); + + virtual bool restoreValue(); + + virtual QList getControls() const; + + public slots: + /// A slot for processing text changed event + void onTextChanged(); + +protected: + /// Saves the internal parameters to the given feature + /// \return True in success + virtual bool storeValueCustom() const; + +private: + /// A line edit control + ExpressionEditor* myEditor; +}; + +#endif /* MODULEBASE_WIDGETEXPREDITOR_H_ */ diff --git a/src/ModuleBase/ModuleBase_WidgetFactory.cpp b/src/ModuleBase/ModuleBase_WidgetFactory.cpp index 93c3e7271..eedb3a390 100644 --- a/src/ModuleBase/ModuleBase_WidgetFactory.cpp +++ b/src/ModuleBase/ModuleBase_WidgetFactory.cpp @@ -11,14 +11,11 @@ #include #include -//#include -//#include #include #include #include #include #include -//#include #include #include #include @@ -31,6 +28,7 @@ #include #include #include +#include #include #include @@ -115,53 +113,45 @@ void ModuleBase_WidgetFactory::createWidget(ModuleBase_PageBase* thePage) thePage->alignToTop(); } -ModuleBase_ModelWidget* ModuleBase_WidgetFactory -::createWidgetByType(const std::string& theType, QWidget* theParent) +ModuleBase_ModelWidget* ModuleBase_WidgetFactory::createWidgetByType(const std::string& theType, + QWidget* theParent) { ModuleBase_ModelWidget* result = NULL; if (theType == WDG_INFO) { result = new ModuleBase_WidgetLabel(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_DOUBLEVALUE) { result = new ModuleBase_WidgetDoubleValue(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_SHAPE_SELECTOR) { - result = new ModuleBase_WidgetShapeSelector(theParent, myWorkshop, myWidgetApi, myParentId); - + result = new ModuleBase_WidgetShapeSelector(theParent, myWorkshop, myWidgetApi, myParentId); } else if (theType == WDG_BOOLVALUE) { result = new ModuleBase_WidgetBoolValue(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_DOUBLEVALUE_EDITOR) { result = new ModuleBase_WidgetEditor(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_FILE_SELECTOR) { result = new ModuleBase_WidgetFileSelector(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_CHOICE) { - result = new ModuleBase_WidgetChoice(theParent, myWidgetApi,myParentId); - + result = new ModuleBase_WidgetChoice(theParent, myWidgetApi, myParentId); } else if (theType == WDG_STRINGVALUE) { - result = new ModuleBase_WidgetLineEdit(theParent, myWidgetApi,myParentId); - + result = new ModuleBase_WidgetLineEdit(theParent, myWidgetApi, myParentId); + } else if (theType == WDG_EXPR_EDITOR) { + result = new ModuleBase_WidgetExprEditor(theParent, myWidgetApi, myParentId); } else if (theType == WDG_MULTISELECTOR) { - result = new ModuleBase_WidgetMultiSelector(theParent, myWorkshop, myWidgetApi,myParentId); - + result = new ModuleBase_WidgetMultiSelector(theParent, myWorkshop, myWidgetApi, myParentId); } else if (theType == WDG_TOOLBOX) { result = new ModuleBase_WidgetToolbox(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_SWITCH) { result = new ModuleBase_WidgetSwitch(theParent, myWidgetApi, myParentId); - } else if (theType == WDG_TOOLBOX_BOX || theType == WDG_SWITCH_CASE) { // Do nothing for "box" and "case" result = NULL; } else { - result = myWorkshop->module()->createWidgetByType(theType, theParent, myWidgetApi, - myParentId); -#ifdef _DEBUG - if (!result) {qDebug("ModuleBase_WidgetFactory::fillWidget: find bad widget type");} -#endif + result = myWorkshop->module()->createWidgetByType(theType, theParent, myWidgetApi, myParentId); + #ifdef _DEBUG + if (!result) { + qDebug("ModuleBase_WidgetFactory::fillWidget: find bad widget type"); + } + #endif } if (result) { myModelWidgets.append(result); diff --git a/src/ModuleBase/ModuleBase_WidgetLineEdit.cpp b/src/ModuleBase/ModuleBase_WidgetLineEdit.cpp index 9c4a1b00f..1f1dbd534 100644 --- a/src/ModuleBase/ModuleBase_WidgetLineEdit.cpp +++ b/src/ModuleBase/ModuleBase_WidgetLineEdit.cpp @@ -33,10 +33,15 @@ ModuleBase_WidgetLineEdit::ModuleBase_WidgetLineEdit(QWidget* theParent, { QFormLayout* aMainLay = new QFormLayout(this); ModuleBase_Tools::adjustMargins(aMainLay); - QString aTitle = QString::fromStdString(theData->widgetLabel()); + QString aLabelText = QString::fromStdString(theData->widgetLabel()); + QString aLabelIcon = QString::fromStdString(theData->widgetIcon()); + QLabel* aLabel = new QLabel(aLabelText, this); + if (!aLabelIcon.isEmpty()) + aLabel->setPixmap(QPixmap(aLabelIcon)); + myLineEdit = new QLineEdit(this); myLineEdit->setMinimumHeight(20); - aMainLay->addRow(aTitle, myLineEdit); + aMainLay->addRow(aLabel, myLineEdit); this->setLayout(aMainLay); connect(myLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(onTextChanged())); diff --git a/src/ParametersPlugin/plugin-Parameters.xml b/src/ParametersPlugin/plugin-Parameters.xml index 63ba2ab68..027f50c24 100644 --- a/src/ParametersPlugin/plugin-Parameters.xml +++ b/src/ParametersPlugin/plugin-Parameters.xml @@ -4,8 +4,8 @@ - - + + diff --git a/src/XGUI/pictures/expression.png b/src/XGUI/pictures/expression.png index 39dd556e638aaf646e1082eb13cf1ffbecba190b..74ab35722a14844fa64fe2d6e110eb92ea572a1d 100644 GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucL5ULAh?3y^w370~qEv>0#LT=By}Z;C1rt33 zJ=4@yqg0@pS)MMAAr}5e0}t{xDDb4PxEC<!TYX%0Gdjlj>H>Q>I27FeLl($z{ynGLb^ol~>g6q8MvtvV^y52bO@9G22 nT|8f0{hGHYPX8tDWWn(A-+}kP8q7<9u4M3Z^>bP0l+XkKT+wXd literal 375 zcmV--0f_#IP) z;c3G#5QQs||5Z1zH!yesd4qa_WP{QP+D=e1LDLDGPGBuy+7U8BHgMm;6Nt7hI;c#E z;Nv9l`CZ_P5;M>9)f&sP7RR)0>re;a3uOe*+ma!~gTZoBdU*-DEEzU&Gfk5z5dhid zRrR*NOwtGoed_1!LQ{qE4sI1g-+__6-_~ZSiS;0blRhx`1bQ|Mz=0Y(fgOAA^Q_|w zA;^hm2lfo982ZA!6Zov)HF{TUibe*3VR4YaPthd+z3oZI`~&;4rT6D;q3L3ME58e|i7wJ^`!R VW