diff options
author | Aleksei German <aleksei.german@qt.io> | 2019-11-13 12:09:01 +0100 |
---|---|---|
committer | Aleksei German <aleksei.german@qt.io> | 2019-11-25 12:24:00 +0000 |
commit | 4bf0105bacb5e948e1539809507e252d564d7e8f (patch) | |
tree | b08e3b9b929edf6f01af0d1a6700cd4f65316f67 /src/plugins/qmldesigner/components | |
parent | d48fa5b8d6c797fb2637ada5fbb0009fd9847355 (diff) | |
download | qt-creator-4bf0105bacb5e948e1539809507e252d564d7e8f.tar.gz |
QmlDesigner Binding Editor structure rework
Separated Binding Editor into 3 files.
Cleaned up includes.
Change-Id: I84d06148b09cfc0df7db0f5a3a99e0f92997b586
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Diffstat (limited to 'src/plugins/qmldesigner/components')
7 files changed, 631 insertions, 482 deletions
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index e56822b2a3..a6fcffa3fb 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -26,393 +26,19 @@ #include "bindingeditor.h" #include <qmldesignerplugin.h> - -#include "texteditorview.h" -#include "texteditorwidget.h" -#include <texteditor/texteditor.h> - #include <coreplugin/icore.h> #include <coreplugin/actionmanager/actionmanager.h> -#include <qmldesigner/qmldesignerplugin.h> -#include <qmldesigner/qmldesignerconstants.h> -#include <qmljseditor/qmljseditor.h> -#include <qmljseditor/qmljseditorconstants.h> -#include <qmljstools/qmljstoolsconstants.h> -#include <qmljseditor/qmljscompletionassist.h> -#include <qmljseditor/qmljshighlighter.h> -#include <qmljseditor/qmljshoverhandler.h> -#include <qmljstools/qmljsindenter.h> -#include <qmljseditor/qmljsautocompleter.h> -#include <qmljseditor/qmljseditordocument.h> -#include <qmljseditor/qmljssemantichighlighter.h> -#include <texteditor/textdocument.h> -#include <texteditor/texteditoractionhandler.h> -#include <texteditor/codeassist/assistinterface.h> -#include <texteditor/codeassist/completionassistprovider.h> -#include <texteditor/syntaxhighlighter.h> -#include <projectexplorer/projectexplorerconstants.h> -#include <coreplugin/editormanager/editormanager.h> #include <metainfo.h> #include <qmlmodelnodeproxy.h> -#include <variantproperty.h> -#include <bindingproperty.h> #include <nodeabstractproperty.h> #include <nodelistproperty.h> #include <propertyeditorvalue.h> -#include <QDialogButtonBox> -#include <QPushButton> -#include <QDebug> -#include <QVBoxLayout> -#include <QHBoxLayout> -#include <QComboBox> -#include <QPlainTextEdit> - namespace QmlDesigner { static BindingEditor *s_lastBindingEditor = nullptr; -const char BINDINGEDITOR_CONTEXT_ID[] = "BindingEditor.BindingEditorContext"; - -BindingEditorWidget::BindingEditorWidget() - : m_context(new BindingEditorContext(this)) -{ - Core::ICore::addContextObject(m_context); - - const Core::Context context(BINDINGEDITOR_CONTEXT_ID); - - /* - * We have to register our own active auto completion shortcut, because the original short cut will - * use the cursor position of the original editor in the editor manager. - */ - - m_completionAction = new QAction(tr("Trigger Completion"), this); - Core::Command *command = Core::ActionManager::registerAction( - m_completionAction, TextEditor::Constants::COMPLETE_THIS, context); - command->setDefaultKeySequence(QKeySequence( - Core::useMacShortcuts - ? tr("Meta+Space") - : tr("Ctrl+Space"))); - - connect(m_completionAction, &QAction::triggered, [this]() { - invokeAssist(TextEditor::Completion); - }); -} - -BindingEditorWidget::~BindingEditorWidget() -{ - unregisterAutoCompletion(); - - Core::ICore::removeContextObject(m_context); - delete m_context; -} - -void BindingEditorWidget::unregisterAutoCompletion() -{ - if (m_completionAction) { - Core::ActionManager::unregisterAction(m_completionAction, TextEditor::Constants::COMPLETE_THIS); - delete m_completionAction; - m_completionAction = nullptr; - } -} - -bool BindingEditorWidget::event(QEvent *event) -{ - if (event->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); - if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { - emit returnKeyClicked(); - return true; - } else { - return QmlJSEditor::QmlJSEditorWidget::event(event); - } - } - return QmlJSEditor::QmlJSEditorWidget::event(event); -} - -TextEditor::AssistInterface *BindingEditorWidget::createAssistInterface( - TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const -{ - Q_UNUSED(assistKind) - return new QmlJSEditor::QmlJSCompletionAssistInterface( - document(), position(), QString(), - assistReason, qmljsdocument->semanticInfo()); -} - -class BindingDocument : public QmlJSEditor::QmlJSEditorDocument -{ -public: - BindingDocument() - : QmlJSEditor::QmlJSEditorDocument(BINDINGEDITOR_CONTEXT_ID) - , m_semanticHighlighter(new QmlJSEditor::SemanticHighlighter(this)) {} - ~BindingDocument() { delete m_semanticHighlighter; } - -protected: - void applyFontSettings() - { - TextDocument::applyFontSettings(); - m_semanticHighlighter->updateFontSettings(fontSettings()); - if (!isSemanticInfoOutdated()) - m_semanticHighlighter->rerun(semanticInfo()); - } - - void triggerPendingUpdates() - { - TextDocument::triggerPendingUpdates(); // calls applyFontSettings if necessary - if (!isSemanticInfoOutdated()) - m_semanticHighlighter->rerun(semanticInfo()); - } - -private: - QmlJSEditor::SemanticHighlighter *m_semanticHighlighter = nullptr; -}; - -class BindingEditorFactory : public TextEditor::TextEditorFactory -{ -public: - BindingEditorFactory() - { - setId(BINDINGEDITOR_CONTEXT_ID); - setDisplayName(QCoreApplication::translate("OpenWith::Editors", BINDINGEDITOR_CONTEXT_ID)); - - - setDocumentCreator([]() { return new BindingDocument; }); - setEditorWidgetCreator([]() { return new BindingEditorWidget; }); - setEditorCreator([]() { return new QmlJSEditor::QmlJSEditor; }); - setAutoCompleterCreator([]() { return new QmlJSEditor::AutoCompleter; }); - setCommentDefinition(Utils::CommentDefinition::CppStyle); - setParenthesesMatchingEnabled(true); - setCodeFoldingSupported(true); - - addHoverHandler(new QmlJSEditor::QmlJSHoverHandler); - setCompletionAssistProvider(new QmlJSEditor::QmlJSCompletionAssistProvider); - } - - static void decorateEditor(TextEditor::TextEditorWidget *editor) - { - editor->textDocument()->setSyntaxHighlighter(new QmlJSEditor::QmlJSHighlighter); - editor->textDocument()->setIndenter(new QmlJSEditor::Internal::Indenter( - editor->textDocument()->document())); - editor->setAutoCompleter(new QmlJSEditor::AutoCompleter); - } -}; - -BindingEditorDialog::BindingEditorDialog(QWidget *parent) - : QDialog(parent) -{ - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - setWindowTitle(tr("Binding Editor")); - setModal(false); - - setupJSEditor(); - setupUIComponents(); - - QObject::connect(m_buttonBox, &QDialogButtonBox::accepted, - this, &BindingEditorDialog::accepted); - QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, - this, &BindingEditorDialog::rejected); - QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked, - this, &BindingEditorDialog::accepted); - - QObject::connect(m_comboBoxItem, QOverload<int>::of(&QComboBox::currentIndexChanged), - this, &BindingEditorDialog::itemIDChanged); - QObject::connect(m_comboBoxProperty, QOverload<int>::of(&QComboBox::currentIndexChanged), - this, &BindingEditorDialog::propertyIDChanged); - QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged, - this, &BindingEditorDialog::textChanged); -} - -BindingEditorDialog::~BindingEditorDialog() -{ - delete m_editor; //m_editorWidget is handled by basetexteditor destructor - delete m_buttonBox; - delete m_comboBoxItem; - delete m_comboBoxProperty; - delete m_comboBoxLayout; - delete m_verticalLayout; -} - -void BindingEditorDialog::showWidget(int x, int y) -{ - this->show(); - this->raise(); - move(QPoint(x, y)); - m_editorWidget->setFocus(); -} - -QString BindingEditorDialog::editorValue() const -{ - if (!m_editorWidget) - return {}; - - return m_editorWidget->document()->toPlainText(); -} - -void BindingEditorDialog::setEditorValue(const QString &text) -{ - if (m_editorWidget) - m_editorWidget->document()->setPlainText(text); -} - -void BindingEditorDialog::setAllBindings(QList<BindingEditorDialog::BindingOption> bindings) -{ - m_lock = true; - - m_bindings = bindings; - setupComboBoxes(); - adjustProperties(); - - m_lock = false; -} - -void BindingEditorDialog::adjustProperties() -{ - const QString expression = editorValue(); - QString item; - QString property; - QStringList expressionElements = expression.split("."); - - if (!expressionElements.isEmpty()) { - const int itemIndex = m_bindings.indexOf(expressionElements.at(0)); - - if (itemIndex != -1) { - item = expressionElements.at(0); - expressionElements.removeFirst(); - - if (!expressionElements.isEmpty()) { - const QString sum = expressionElements.join("."); - - if (m_bindings.at(itemIndex).properties.contains(sum)) - property = sum; - } - } - } - - if (item.isEmpty()) { - item = undefinedString; - if (m_comboBoxItem->findText(item) == -1) - m_comboBoxItem->addItem(item); - } - m_comboBoxItem->setCurrentText(item); - - if (property.isEmpty()) { - property = undefinedString; - if (m_comboBoxProperty->findText(property) == -1) - m_comboBoxProperty->addItem(property); - } - m_comboBoxProperty->setCurrentText(property); -} - -void BindingEditorDialog::unregisterAutoCompletion() -{ - if (m_editorWidget) - m_editorWidget->unregisterAutoCompletion(); -} - -void BindingEditorDialog::setupJSEditor() -{ - static BindingEditorFactory f; - m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor()); - m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget()); - - Core::Context context = m_editor->context(); - context.prepend(BINDINGEDITOR_CONTEXT_ID); - m_editorWidget->m_context->setContext(context); - - auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor(); - - m_editorWidget->qmljsdocument = qobject_cast<QmlJSEditor::QmlJSEditorWidget *>( - qmlDesignerEditor->widget())->qmlJsEditorDocument(); - - - m_editorWidget->setLineNumbersVisible(false); - m_editorWidget->setMarksVisible(false); - m_editorWidget->setCodeFoldingSupported(false); - m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_editorWidget->setTabChangesFocus(true); -} - -void BindingEditorDialog::setupUIComponents() -{ - m_verticalLayout = new QVBoxLayout(this); - m_comboBoxLayout = new QHBoxLayout; - - m_comboBoxItem = new QComboBox(this); - m_comboBoxProperty = new QComboBox(this); - - m_editorWidget->setParent(this); - m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); - m_editorWidget->show(); - - m_buttonBox = new QDialogButtonBox(this); - m_buttonBox->setOrientation(Qt::Horizontal); - m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); - - - m_comboBoxLayout->addWidget(m_comboBoxItem); - m_comboBoxLayout->addWidget(m_comboBoxProperty); - - m_verticalLayout->addLayout(m_comboBoxLayout); - m_verticalLayout->addWidget(m_editorWidget); - m_verticalLayout->addWidget(m_buttonBox); - - this->resize(660, 240); -} - -void BindingEditorDialog::setupComboBoxes() -{ - m_comboBoxItem->clear(); - m_comboBoxProperty->clear(); - - for (auto bind : m_bindings) - m_comboBoxItem->addItem(bind.item); -} - -void BindingEditorDialog::itemIDChanged(int itemID) -{ - const QString previousProperty = m_comboBoxProperty->currentText(); - m_comboBoxProperty->clear(); - - if (m_bindings.size() > itemID && itemID != -1) { - m_comboBoxProperty->addItems(m_bindings.at(itemID).properties); - - if (!m_lock) - if (m_comboBoxProperty->findText(previousProperty) != -1) - m_comboBoxProperty->setCurrentText(previousProperty); - - const int undefinedItem = m_comboBoxItem->findText(undefinedString); - if ((undefinedItem != -1) && (m_comboBoxItem->itemText(itemID) != undefinedString)) - m_comboBoxItem->removeItem(undefinedItem); - } -} - -void BindingEditorDialog::propertyIDChanged(int propertyID) -{ - const int itemID = m_comboBoxItem->currentIndex(); - - if (!m_lock) - if (!m_comboBoxProperty->currentText().isEmpty() && (m_comboBoxProperty->currentText() != undefinedString)) - setEditorValue(m_comboBoxItem->itemText(itemID) + "." + m_comboBoxProperty->itemText(propertyID)); - - const int undefinedProperty = m_comboBoxProperty->findText(undefinedString); - if ((undefinedProperty != -1) && (m_comboBoxProperty->itemText(propertyID) != undefinedString)) - m_comboBoxProperty->removeItem(undefinedProperty); -} - -void BindingEditorDialog::textChanged() -{ - if (m_lock) - return; - - m_lock = true; - adjustProperties(); - m_lock = false; -} - - BindingEditor::BindingEditor(QObject *) { } @@ -556,5 +182,4 @@ QVariant BindingEditor::stateModelNode() const return m_stateModelNode; } - -} +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h index a1deb74108..5da06d2ecf 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.h @@ -26,117 +26,16 @@ #ifndef BINDINGEDITOR_H #define BINDINGEDITOR_H -#include "texteditorview.h" -#include <texteditor/texteditor.h> -#include <QtQml> +#include <bindingeditor/bindingeditordialog.h> +#include <qmldesignercorelib_global.h> +#include <modelnode.h> -#include <QWidget> -#include <QDialog> +#include <QtQml> +#include <QObject> #include <QPointer> -#include <qmljseditor/qmljseditor.h> - -#include <memory> - -QT_BEGIN_NAMESPACE -class QTextEdit; -class QDialogButtonBox; -class QVBoxLayout; -class QHBoxLayout; -class QComboBox; -QT_END_NAMESPACE - namespace QmlDesigner { -class BindingEditorContext : public Core::IContext -{ - Q_OBJECT - -public: - BindingEditorContext(QWidget *parent) : Core::IContext(parent) - { - setWidget(parent); - } -}; - -class BindingEditorWidget : public QmlJSEditor::QmlJSEditorWidget -{ - Q_OBJECT - -public: - BindingEditorWidget(); - ~BindingEditorWidget() override; - - void unregisterAutoCompletion(); - - bool event(QEvent *event) override; - - TextEditor::AssistInterface *createAssistInterface(TextEditor::AssistKind assistKind, - TextEditor::AssistReason assistReason) const override; - -signals: - void returnKeyClicked(); - -public: - QmlJSEditor::QmlJSEditorDocument *qmljsdocument = nullptr; - BindingEditorContext *m_context = nullptr; - QAction *m_completionAction = nullptr; -}; - -class BindingEditorDialog : public QDialog -{ - Q_OBJECT - -public: - struct BindingOption - { - BindingOption() {} - BindingOption(const QString &value) { item = value; } - - bool operator==(const QString &value) const { return value == item; } - bool operator==(const BindingOption &value) const { return value.item == item; } - - QString item; - QStringList properties; - }; - -public: - BindingEditorDialog(QWidget *parent = nullptr); - ~BindingEditorDialog() override; - - void showWidget(int x, int y); - - QString editorValue() const; - void setEditorValue(const QString &text); - - void setAllBindings(QList<BindingEditorDialog::BindingOption> bindings); - void adjustProperties(); - - void unregisterAutoCompletion(); - -private: - void setupJSEditor(); - void setupUIComponents(); - void setupComboBoxes(); - -public slots: - void itemIDChanged(int); - void propertyIDChanged(int); - void textChanged(); - -private: - TextEditor::BaseTextEditor *m_editor = nullptr; - BindingEditorWidget *m_editorWidget = nullptr; - QVBoxLayout *m_verticalLayout = nullptr; - QDialogButtonBox *m_buttonBox = nullptr; - QHBoxLayout *m_comboBoxLayout = nullptr; - QComboBox *m_comboBoxItem = nullptr; - QComboBox *m_comboBoxProperty = nullptr; - QList<BindingEditorDialog::BindingOption> m_bindings; - bool m_lock = false; - const QString undefinedString = {"[Undefined]"}; -}; - class BindingEditor : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri index 518905eb2a..88b2897c7b 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri @@ -1,3 +1,7 @@ HEADERS += $$PWD/bindingeditor.h +HEADERS += $$PWD/bindingeditordialog.h +HEADERS += $$PWD/bindingeditorwidget.h SOURCES += $$PWD/bindingeditor.cpp +SOURCES += $$PWD/bindingeditordialog.cpp +SOURCES += $$PWD/bindingeditorwidget.cpp diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp new file mode 100644 index 0000000000..08cb5d8bd3 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "bindingeditordialog.h" + +#include <texteditor/texteditor.h> + +#include <qmldesigner/qmldesignerplugin.h> +#include <qmljseditor/qmljseditor.h> +#include <qmljseditor/qmljseditordocument.h> +#include <texteditor/textdocument.h> + +#include <QDialogButtonBox> +#include <QPushButton> +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QComboBox> +#include <QPlainTextEdit> + +namespace QmlDesigner { + +BindingEditorDialog::BindingEditorDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Binding Editor")); + setModal(false); + + setupJSEditor(); + setupUIComponents(); + + QObject::connect(m_buttonBox, &QDialogButtonBox::accepted, + this, &BindingEditorDialog::accepted); + QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, + this, &BindingEditorDialog::rejected); + QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked, + this, &BindingEditorDialog::accepted); + + QObject::connect(m_comboBoxItem, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &BindingEditorDialog::itemIDChanged); + QObject::connect(m_comboBoxProperty, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &BindingEditorDialog::propertyIDChanged); + QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged, + this, &BindingEditorDialog::textChanged); +} + +BindingEditorDialog::~BindingEditorDialog() +{ + delete m_editor; //m_editorWidget is handled by basetexteditor destructor + delete m_buttonBox; + delete m_comboBoxItem; + delete m_comboBoxProperty; + delete m_comboBoxLayout; + delete m_verticalLayout; +} + +void BindingEditorDialog::showWidget(int x, int y) +{ + this->show(); + this->raise(); + move(QPoint(x, y)); + m_editorWidget->setFocus(); +} + +QString BindingEditorDialog::editorValue() const +{ + if (!m_editorWidget) + return {}; + + return m_editorWidget->document()->toPlainText(); +} + +void BindingEditorDialog::setEditorValue(const QString &text) +{ + if (m_editorWidget) + m_editorWidget->document()->setPlainText(text); +} + +void BindingEditorDialog::setAllBindings(QList<BindingEditorDialog::BindingOption> bindings) +{ + m_lock = true; + + m_bindings = bindings; + setupComboBoxes(); + adjustProperties(); + + m_lock = false; +} + +void BindingEditorDialog::adjustProperties() +{ + const QString expression = editorValue(); + QString item; + QString property; + QStringList expressionElements = expression.split("."); + + if (!expressionElements.isEmpty()) { + const int itemIndex = m_bindings.indexOf(expressionElements.at(0)); + + if (itemIndex != -1) { + item = expressionElements.at(0); + expressionElements.removeFirst(); + + if (!expressionElements.isEmpty()) { + const QString sum = expressionElements.join("."); + + if (m_bindings.at(itemIndex).properties.contains(sum)) + property = sum; + } + } + } + + if (item.isEmpty()) { + item = undefinedString; + if (m_comboBoxItem->findText(item) == -1) + m_comboBoxItem->addItem(item); + } + m_comboBoxItem->setCurrentText(item); + + if (property.isEmpty()) { + property = undefinedString; + if (m_comboBoxProperty->findText(property) == -1) + m_comboBoxProperty->addItem(property); + } + m_comboBoxProperty->setCurrentText(property); +} + +void BindingEditorDialog::unregisterAutoCompletion() +{ + if (m_editorWidget) + m_editorWidget->unregisterAutoCompletion(); +} + +void BindingEditorDialog::setupJSEditor() +{ + static BindingEditorFactory f; + m_editor = qobject_cast<TextEditor::BaseTextEditor*>(f.createEditor()); + m_editorWidget = qobject_cast<BindingEditorWidget*>(m_editor->editorWidget()); + + Core::Context context = m_editor->context(); + context.prepend(BINDINGEDITOR_CONTEXT_ID); + m_editorWidget->m_context->setContext(context); + + auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor(); + + m_editorWidget->qmljsdocument = qobject_cast<QmlJSEditor::QmlJSEditorWidget *>( + qmlDesignerEditor->widget())->qmlJsEditorDocument(); + + + m_editorWidget->setLineNumbersVisible(false); + m_editorWidget->setMarksVisible(false); + m_editorWidget->setCodeFoldingSupported(false); + m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_editorWidget->setTabChangesFocus(true); +} + +void BindingEditorDialog::setupUIComponents() +{ + m_verticalLayout = new QVBoxLayout(this); + m_comboBoxLayout = new QHBoxLayout; + + m_comboBoxItem = new QComboBox(this); + m_comboBoxProperty = new QComboBox(this); + + m_editorWidget->setParent(this); + m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + m_editorWidget->show(); + + m_buttonBox = new QDialogButtonBox(this); + m_buttonBox->setOrientation(Qt::Horizontal); + m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + + m_comboBoxLayout->addWidget(m_comboBoxItem); + m_comboBoxLayout->addWidget(m_comboBoxProperty); + + m_verticalLayout->addLayout(m_comboBoxLayout); + m_verticalLayout->addWidget(m_editorWidget); + m_verticalLayout->addWidget(m_buttonBox); + + this->resize(660, 240); +} + +void BindingEditorDialog::setupComboBoxes() +{ + m_comboBoxItem->clear(); + m_comboBoxProperty->clear(); + + for (auto bind : m_bindings) + m_comboBoxItem->addItem(bind.item); +} + +void BindingEditorDialog::itemIDChanged(int itemID) +{ + const QString previousProperty = m_comboBoxProperty->currentText(); + m_comboBoxProperty->clear(); + + if (m_bindings.size() > itemID && itemID != -1) { + m_comboBoxProperty->addItems(m_bindings.at(itemID).properties); + + if (!m_lock) + if (m_comboBoxProperty->findText(previousProperty) != -1) + m_comboBoxProperty->setCurrentText(previousProperty); + + const int undefinedItem = m_comboBoxItem->findText(undefinedString); + if ((undefinedItem != -1) && (m_comboBoxItem->itemText(itemID) != undefinedString)) + m_comboBoxItem->removeItem(undefinedItem); + } +} + +void BindingEditorDialog::propertyIDChanged(int propertyID) +{ + const int itemID = m_comboBoxItem->currentIndex(); + + if (!m_lock) + if (!m_comboBoxProperty->currentText().isEmpty() && (m_comboBoxProperty->currentText() != undefinedString)) + setEditorValue(m_comboBoxItem->itemText(itemID) + "." + m_comboBoxProperty->itemText(propertyID)); + + const int undefinedProperty = m_comboBoxProperty->findText(undefinedString); + if ((undefinedProperty != -1) && (m_comboBoxProperty->itemText(propertyID) != undefinedString)) + m_comboBoxProperty->removeItem(undefinedProperty); +} + +void BindingEditorDialog::textChanged() +{ + if (m_lock) + return; + + m_lock = true; + adjustProperties(); + m_lock = false; +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h new file mode 100644 index 0000000000..f1b224a716 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#ifndef BINDINGEDITORDIALOG_H +#define BINDINGEDITORDIALOG_H + +#include <bindingeditor/bindingeditorwidget.h> +#include <texteditor/texteditor.h> + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QVBoxLayout; +class QHBoxLayout; +class QComboBox; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class BindingEditorDialog : public QDialog +{ + Q_OBJECT + +public: + struct BindingOption + { + BindingOption() {} + BindingOption(const QString &value) { item = value; } + + bool operator==(const QString &value) const { return value == item; } + bool operator==(const BindingOption &value) const { return value.item == item; } + + QString item; + QStringList properties; + }; + +public: + BindingEditorDialog(QWidget *parent = nullptr); + ~BindingEditorDialog() override; + + void showWidget(int x, int y); + + QString editorValue() const; + void setEditorValue(const QString &text); + + void setAllBindings(QList<BindingEditorDialog::BindingOption> bindings); + void adjustProperties(); + + void unregisterAutoCompletion(); + +private: + void setupJSEditor(); + void setupUIComponents(); + void setupComboBoxes(); + +public slots: + void itemIDChanged(int); + void propertyIDChanged(int); + void textChanged(); + +private: + TextEditor::BaseTextEditor *m_editor = nullptr; + BindingEditorWidget *m_editorWidget = nullptr; + QVBoxLayout *m_verticalLayout = nullptr; + QDialogButtonBox *m_buttonBox = nullptr; + QHBoxLayout *m_comboBoxLayout = nullptr; + QComboBox *m_comboBoxItem = nullptr; + QComboBox *m_comboBoxProperty = nullptr; + QList<BindingEditorDialog::BindingOption> m_bindings; + bool m_lock = false; + const QString undefinedString = {"[Undefined]"}; +}; + +} + +#endif //BINDINGEDITORDIALOG_H diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp new file mode 100644 index 0000000000..6a058cb49a --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "bindingeditorwidget.h" + +#include <coreplugin/icore.h> +#include <coreplugin/actionmanager/actionmanager.h> +#include <qmljseditor/qmljseditor.h> +#include <qmljseditor/qmljscompletionassist.h> +#include <qmljseditor/qmljshighlighter.h> +#include <qmljseditor/qmljshoverhandler.h> +#include <qmljseditor/qmljsautocompleter.h> +#include <qmljseditor/qmljseditordocument.h> +#include <qmljseditor/qmljssemantichighlighter.h> +#include <qmljstools/qmljsindenter.h> + +#include <QAction> + +namespace QmlDesigner { + +BindingEditorWidget::BindingEditorWidget() + : m_context(new BindingEditorContext(this)) +{ + Core::ICore::addContextObject(m_context); + + const Core::Context context(BINDINGEDITOR_CONTEXT_ID); + + /* + * We have to register our own active auto completion shortcut, because the original short cut will + * use the cursor position of the original editor in the editor manager. + */ + + m_completionAction = new QAction(tr("Trigger Completion"), this); + Core::Command *command = Core::ActionManager::registerAction( + m_completionAction, TextEditor::Constants::COMPLETE_THIS, context); + command->setDefaultKeySequence(QKeySequence( + Core::useMacShortcuts + ? tr("Meta+Space") + : tr("Ctrl+Space"))); + + connect(m_completionAction, &QAction::triggered, [this]() { + invokeAssist(TextEditor::Completion); + }); +} + +BindingEditorWidget::~BindingEditorWidget() +{ + unregisterAutoCompletion(); + + Core::ICore::removeContextObject(m_context); + delete m_context; +} + +void BindingEditorWidget::unregisterAutoCompletion() +{ + if (m_completionAction) { + Core::ActionManager::unregisterAction(m_completionAction, TextEditor::Constants::COMPLETE_THIS); + delete m_completionAction; + m_completionAction = nullptr; + } +} + +bool BindingEditorWidget::event(QEvent *event) +{ + if (event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) { + emit returnKeyClicked(); + return true; + } else { + return QmlJSEditor::QmlJSEditorWidget::event(event); + } + } + return QmlJSEditor::QmlJSEditorWidget::event(event); +} + +TextEditor::AssistInterface *BindingEditorWidget::createAssistInterface( + TextEditor::AssistKind assistKind, TextEditor::AssistReason assistReason) const +{ + Q_UNUSED(assistKind) + return new QmlJSEditor::QmlJSCompletionAssistInterface( + document(), position(), QString(), + assistReason, qmljsdocument->semanticInfo()); +} + +BindingDocument::BindingDocument() + : QmlJSEditor::QmlJSEditorDocument(BINDINGEDITOR_CONTEXT_ID) + , m_semanticHighlighter(new QmlJSEditor::SemanticHighlighter(this)) +{ + +} + +BindingDocument::~BindingDocument() +{ + delete m_semanticHighlighter; +} + +void BindingDocument::applyFontSettings() +{ + TextDocument::applyFontSettings(); + m_semanticHighlighter->updateFontSettings(fontSettings()); + if (!isSemanticInfoOutdated()) + m_semanticHighlighter->rerun(semanticInfo()); +} + +void BindingDocument::triggerPendingUpdates() +{ + TextDocument::triggerPendingUpdates(); // calls applyFontSettings if necessary + if (!isSemanticInfoOutdated()) + m_semanticHighlighter->rerun(semanticInfo()); +} + +BindingEditorFactory::BindingEditorFactory() +{ + setId(BINDINGEDITOR_CONTEXT_ID); + setDisplayName(QCoreApplication::translate("OpenWith::Editors", QmlDesigner::BINDINGEDITOR_CONTEXT_ID)); + + setDocumentCreator([]() { return new BindingDocument; }); + setEditorWidgetCreator([]() { return new BindingEditorWidget; }); + setEditorCreator([]() { return new QmlJSEditor::QmlJSEditor; }); + setAutoCompleterCreator([]() { return new QmlJSEditor::AutoCompleter; }); + setCommentDefinition(Utils::CommentDefinition::CppStyle); + setParenthesesMatchingEnabled(true); + setCodeFoldingSupported(true); + + addHoverHandler(new QmlJSEditor::QmlJSHoverHandler); + setCompletionAssistProvider(new QmlJSEditor::QmlJSCompletionAssistProvider); +} + +void BindingEditorFactory::decorateEditor(TextEditor::TextEditorWidget *editor) +{ + editor->textDocument()->setSyntaxHighlighter(new QmlJSEditor::QmlJSHighlighter); + editor->textDocument()->setIndenter(new QmlJSEditor::Internal::Indenter( + editor->textDocument()->document())); + editor->setAutoCompleter(new QmlJSEditor::AutoCompleter); +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h new file mode 100644 index 0000000000..597051d89f --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#ifndef BINDINGEDITORWIDGET_H +#define BINDINGEDITORWIDGET_H + +#include <texteditor/texteditor.h> +#include <qmljseditor/qmljseditordocument.h> +#include <qmljseditor/qmljssemantichighlighter.h> +#include <qmljseditor/qmljseditor.h> + +#include <QtQml> +#include <QWidget> +#include <QDialog> + +namespace QmlDesigner { + +const char BINDINGEDITOR_CONTEXT_ID[] = "BindingEditor.BindingEditorContext"; + + +class BindingEditorContext : public Core::IContext +{ + Q_OBJECT + +public: + BindingEditorContext(QWidget *parent) : Core::IContext(parent) + { + setWidget(parent); + } +}; + +class BindingEditorWidget : public QmlJSEditor::QmlJSEditorWidget +{ + Q_OBJECT + +public: + BindingEditorWidget(); + ~BindingEditorWidget() override; + + void unregisterAutoCompletion(); + + bool event(QEvent *event) override; + + TextEditor::AssistInterface *createAssistInterface(TextEditor::AssistKind assistKind, + TextEditor::AssistReason assistReason) const override; + +signals: + void returnKeyClicked(); + +public: + QmlJSEditor::QmlJSEditorDocument *qmljsdocument = nullptr; + BindingEditorContext *m_context = nullptr; + QAction *m_completionAction = nullptr; +}; + +class BindingDocument : public QmlJSEditor::QmlJSEditorDocument +{ +public: + BindingDocument(); + ~BindingDocument(); + +protected: + void applyFontSettings(); + + void triggerPendingUpdates(); + +private: + QmlJSEditor::SemanticHighlighter *m_semanticHighlighter = nullptr; +}; + + +class BindingEditorFactory : public TextEditor::TextEditorFactory +{ +public: + BindingEditorFactory(); + + static void decorateEditor(TextEditor::TextEditorWidget *editor); +}; + +} + +#endif //BINDINGEDITORWIDGET_H |