File indexing completed on 2024-05-19 15:46:45
0001 /* 0002 SPDX-FileCopyrightText: 2013 Sven Brauch <svenbrauch@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "propertypreviewwidget.h" 0008 0009 #include <QQuickWidget> 0010 #include <QQuickItem> 0011 #include <QQmlContext> 0012 #include <QHBoxLayout> 0013 #include <QLabel> 0014 #include <QStandardPaths> 0015 0016 #include <KLocalizedString> 0017 #include <KLocalizedContext> 0018 #include <KTextEditor/Document> 0019 #include <KTextEditor/View> 0020 #include <KDeclarative/KDeclarative> 0021 0022 #include <language/duchain/ducontext.h> 0023 #include <language/duchain/duchainlock.h> 0024 0025 // List of supported properties. The string must be the name of the property, 0026 // which can contain dots if necessary 0027 QMultiHash<QString, SupportedProperty> PropertyPreviewWidget::supportedProperties; 0028 0029 QWidget* PropertyPreviewWidget::constructIfPossible(KTextEditor::Document* doc, 0030 const KTextEditor::Range& keyRange, 0031 const KTextEditor::Range& valueRange, 0032 Declaration* decl, 0033 const QString& key, 0034 const QString& value) 0035 { 0036 #define PROP(key, filename, type, class) \ 0037 supportedProperties.insert(key, SupportedProperty(QUrl(base + filename), type, class)); 0038 0039 if ( supportedProperties.isEmpty() ) { 0040 QString base = QStandardPaths::locate( 0041 QStandardPaths::GenericDataLocation, 0042 QStringLiteral("kdevqmljssupport/propertywidgets"), 0043 QStandardPaths::LocateDirectory 0044 ) + QLatin1Char('/'); 0045 0046 // Positioning 0047 PROP(QStringLiteral("width"), QLatin1String("Width.qml"), QString(), QString()) 0048 PROP(QStringLiteral("height"), QLatin1String("Height.qml"), QString(), QString()) 0049 PROP(QStringLiteral("spacing"), QLatin1String("Spacing.qml"), QString(), QString()) 0050 0051 // Margins 0052 PROP(QStringLiteral("margins"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QQuickAnchors")); // matches anchors.margins and anchors { margins: } 0053 PROP(QStringLiteral("margins"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QDeclarativeAnchors")); 0054 PROP(QStringLiteral("leftMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QQuickAnchors")); 0055 PROP(QStringLiteral("leftMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QDeclarativeAnchors")); 0056 PROP(QStringLiteral("rightMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QQuickAnchors")); 0057 PROP(QStringLiteral("rightMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QDeclarativeAnchors")); 0058 PROP(QStringLiteral("topMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QQuickAnchors")); 0059 PROP(QStringLiteral("topMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QDeclarativeAnchors")); 0060 PROP(QStringLiteral("bottomMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QQuickAnchors")); 0061 PROP(QStringLiteral("bottomMargin"), QLatin1String("Spacing.qml"), QString(), QStringLiteral("QDeclarativeAnchors")); 0062 0063 // Animations 0064 PROP(QStringLiteral("duration"), QLatin1String("Duration.qml"), QString(), QString()) 0065 0066 // Font QDeclarativeFontValueType, QQuickFontValueType 0067 PROP(QStringLiteral("family"), QLatin1String("FontFamily.qml"), QString(), QStringLiteral("QDeclarativeFontValueType")) 0068 PROP(QStringLiteral("family"), QLatin1String("FontFamily.qml"), QString(), QStringLiteral("QQuickFontValueType")) 0069 PROP(QStringLiteral("pointSize"), QLatin1String("FontSize.qml"), QString(), QStringLiteral("QDeclarativeFontValueType")) 0070 PROP(QStringLiteral("pointSize"), QLatin1String("FontSize.qml"), QString(), QStringLiteral("QQuickFontValueType")) 0071 0072 // Appearance 0073 PROP(QStringLiteral("opacity"), QLatin1String("Opacity.qml"), QString(), QString()) 0074 0075 // Type-dependent widgets 0076 PROP(QString(), QLatin1String("ColorPicker.qml"), QStringLiteral("color"), QString()) 0077 } 0078 #undef PROP 0079 0080 QList<SupportedProperty> properties; 0081 0082 properties << supportedProperties.values(key.section(QLatin1Char('.'), -1, -1)); 0083 properties << supportedProperties.values(QString()); 0084 0085 // Explore each possible supported property and return the first supported widget 0086 DUChainReadLocker lock; 0087 0088 for (const SupportedProperty& property : qAsConst(properties)) { 0089 if (!decl || !decl->abstractType() || !decl->context() || !decl->context()->owner()) { 0090 continue; 0091 } 0092 0093 if (!decl->abstractType()->toString().contains(property.typeContains)) { 0094 continue; 0095 } 0096 0097 if (!decl->context()->owner()->toString().contains(property.classContains)) { 0098 continue; 0099 } 0100 0101 return new PropertyPreviewWidget(doc, keyRange, valueRange, property, value); 0102 } 0103 0104 return nullptr; 0105 } 0106 0107 void PropertyPreviewWidget::updateValue() 0108 { 0109 QString newValue = view->rootObject()->property("value").toString(); 0110 0111 // set the cursor to the edited range, otherwise the view will jump if we call doc->endEditing() 0112 //document->activeView()->setCursorPosition(KTextEditor::Cursor(valueRange.start.line, valueRange.start.column)); 0113 if (valueRange.end().column() - valueRange.start().column() == newValue.size()) { 0114 document->replaceText(valueRange, newValue); 0115 } 0116 else { 0117 // the length of the text changed so don't replace it but remove the old 0118 // and insert the new text. 0119 KTextEditor::Document::EditingTransaction transaction(document); 0120 document->removeText(valueRange); 0121 document->insertText(valueRange.start(), newValue); 0122 0123 valueRange.setRange( 0124 valueRange.start(), 0125 KTextEditor::Cursor(valueRange.start().line(), valueRange.start().column() + newValue.size()) 0126 ); 0127 } 0128 } 0129 0130 PropertyPreviewWidget::~PropertyPreviewWidget() 0131 { 0132 } 0133 0134 PropertyPreviewWidget::PropertyPreviewWidget(KTextEditor::Document* doc, 0135 const KTextEditor::Range& keyRange, const KTextEditor::Range& valueRange, 0136 const SupportedProperty& property, const QString& value) 0137 : QWidget() 0138 , view(new QQuickWidget) 0139 , document(doc) 0140 , keyRange(keyRange) 0141 , valueRange(valueRange) 0142 , property(property) 0143 { 0144 //setup kdeclarative library 0145 KDeclarative::KDeclarative::setupEngine(view->engine()); 0146 KLocalizedContext *localizedContextObject = new KLocalizedContext(view->engine()); 0147 localizedContextObject->setTranslationDomain(QStringLiteral("kdevqmljs")); 0148 view->engine()->rootContext()->setContextObject(localizedContextObject); 0149 0150 // Configure layout 0151 auto l = new QHBoxLayout; 0152 0153 l->setContentsMargins(0, 0, 0, 0); 0154 setLayout(l); 0155 0156 // see docstring for ILanguageSupport::specialLanguageObjectNavigationWidget 0157 setProperty("DoNotCloseOnCursorMove", true); 0158 0159 view->setSource(property.qmlfile); 0160 0161 if (!view->rootObject()) { 0162 // don't crash because of a syntax error or missing QML file 0163 l->addWidget(new QLabel(i18n("Error loading QML file: %1", property.qmlfile.path()))); 0164 delete view; 0165 return; 0166 } 0167 0168 // set the initial value read from the document 0169 view->rootObject()->setProperty("initialValue", value); 0170 0171 // connect to the slot which has to be emitted from QML when the value changes 0172 QObject::connect(view->rootObject(), SIGNAL(valueChanged()), 0173 this, SLOT(updateValue())); 0174 l->addWidget(view); 0175 } 0176 0177 #include "moc_propertypreviewwidget.cpp"