From a98e3ba6238788e5b94dfda7d63a9df380b905af Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 17 Jul 2015 14:21:55 +0200 Subject: Qt Designer: Add customizable property tooltips. - Introduce tag propertytooltip to property specifications in the ui XML snippet returned by QDesignerCustomWidgetInterface::domXml() - Pass the text to the property editor and set as description tooltip on the property. - As an added benefit, show the type in the normal tooltip. - Demonstrate using TicTacToe example. [ChangeLog][Qt Designer] Added customizable property tooltips. Task-number: QTBUG-45442 Change-Id: I371bbbb3a6f2bc0f433193b5eb45658211ca67de Reviewed-by: Jarek Kobus --- .../designer/taskmenuextension/tictactoeplugin.cpp | 1 + src/designer/data/ui4.xsd | 5 ++ .../components/propertyeditor/propertyeditor.cpp | 59 +++++++++++---- .../src/designer/doc/src/designer-manual.qdoc | 13 +++- src/designer/src/lib/shared/pluginmanager.cpp | 69 ++++++++++------- src/designer/src/lib/shared/pluginmanager_p.h | 2 + src/designer/src/lib/uilib/ui4.cpp | 86 ++++++++++++++++++++++ src/designer/src/lib/uilib/ui4_p.h | 40 +++++++++- 8 files changed, 228 insertions(+), 47 deletions(-) diff --git a/examples/designer/taskmenuextension/tictactoeplugin.cpp b/examples/designer/taskmenuextension/tictactoeplugin.cpp index 770a44a24..6e0c7b02d 100644 --- a/examples/designer/taskmenuextension/tictactoeplugin.cpp +++ b/examples/designer/taskmenuextension/tictactoeplugin.cpp @@ -127,6 +127,7 @@ QString TicTacToePlugin::domXml() const \ TicTacToe\ \ + Tic Tac Toe state\ \ \ \ diff --git a/src/designer/data/ui4.xsd b/src/designer/data/ui4.xsd index 62a69429f..e5474a65c 100644 --- a/src/designer/data/ui4.xsd +++ b/src/designer/data/ui4.xsd @@ -579,10 +579,15 @@ + + + + + diff --git a/src/designer/src/components/propertyeditor/propertyeditor.cpp b/src/designer/src/components/propertyeditor/propertyeditor.cpp index 75ecf70b5..91e702362 100644 --- a/src/designer/src/components/propertyeditor/propertyeditor.cpp +++ b/src/designer/src/components/propertyeditor/propertyeditor.cpp @@ -56,9 +56,8 @@ #include #include #include -#ifdef Q_OS_WIN -# include -#endif +#include + #include #include #include @@ -909,22 +908,36 @@ QString PropertyEditor::realClassName(QObject *object) const return className; } -static QString msgUnsupportedType(const QString &propertyName, unsigned type) +static const char *typeName(int type) +{ + if (type == qMetaTypeId()) + type = QVariant::String; + if (type < int(QVariant::UserType)) + return QVariant::typeToName(static_cast(type)); + if (type == qMetaTypeId()) + return "QIcon"; + if (type == qMetaTypeId()) + return "QPixmap"; + if (type == qMetaTypeId()) + return "QKeySequence"; + if (type == qMetaTypeId()) + return "QFlags"; + if (type == qMetaTypeId()) + return "enum"; + if (type == QVariant::Invalid) + return "invalid"; + if (type == QVariant::UserType) + return "user type"; + return Q_NULLPTR; +} + +static QString msgUnsupportedType(const QString &propertyName, int type) { QString rc; QTextStream str(&rc); - str << "The property \"" << propertyName << "\" of type " << type; - if (type == QVariant::Invalid) { - str << " (invalid) "; - } else { - if (type < QVariant::UserType) { - if (const char *typeName = QVariant::typeToName(static_cast(type))) - str << " (" << typeName << ") "; - } else { - str << " (user type) "; - } - } - str << " is not supported yet!"; + const char *typeS = typeName(type); + str << "The property \"" << propertyName << "\" of type (" + << (typeS ? typeS : "unknown") << ") is not supported yet!"; return rc; } @@ -998,6 +1011,9 @@ void PropertyEditor::setObject(QObject *object) m_groups.clear(); if (m_propertySheet) { + const QString className = WidgetFactory::classNameOf(formWindow->core(), m_object); + const QDesignerCustomWidgetData customData = formWindow->core()->pluginManager()->customWidgetData(className); + QtProperty *lastProperty = 0; QtProperty *lastGroup = 0; const int propertyCount = m_propertySheet->count(); @@ -1048,6 +1064,17 @@ void PropertyEditor::setObject(QObject *object) if (property != 0) { const bool dynamicProperty = (dynamicSheet && dynamicSheet->isDynamicProperty(i)) || (sheet && sheet->isDefaultDynamicProperty(i)); + QString descriptionToolTip; + if (!dynamicProperty && !customData.isNull()) + descriptionToolTip = customData.propertyToolTip(propertyName); + if (descriptionToolTip.isEmpty()) { + if (const char *typeS = typeName(type)) { + descriptionToolTip = propertyName + QLatin1String(" (") + + QLatin1String(typeS) + QLatin1Char(')'); + } + } + if (!descriptionToolTip.isEmpty()) + property->setDescriptionToolTip(descriptionToolTip); switch (type) { case QVariant::Palette: setupPaletteProperty(property); diff --git a/src/designer/src/designer/doc/src/designer-manual.qdoc b/src/designer/src/designer/doc/src/designer-manual.qdoc index 665456468..418a68532 100644 --- a/src/designer/src/designer/doc/src/designer-manual.qdoc +++ b/src/designer/src/designer/doc/src/designer-manual.qdoc @@ -2405,8 +2405,9 @@ pixmap property in the property editor. widgets::MyWidget addPage - + + Explanatory text to be shown in Property Editor @@ -2443,9 +2444,13 @@ pixmap property in the property editor. for them. The \c{} element can contain a list of property meta information. - Currently, properties of type string are supported. For these properties, the - \c{} tag can be used. This tag has the following attributes: + The tag \c{} may be used to specify a tool tip to be shown in Property Editor + when hovering over the property. The property name is given in the attribute \c name and + the element text is the tooltip. This functionality was added in Qt 5.6. + + For properties of type string, the \c{} tag can be used. + This tag has the following attributes: \table \header diff --git a/src/designer/src/lib/shared/pluginmanager.cpp b/src/designer/src/lib/shared/pluginmanager.cpp index 856e07cf4..0b1932d6d 100644 --- a/src/designer/src/lib/shared/pluginmanager.cpp +++ b/src/designer/src/lib/shared/pluginmanager.cpp @@ -67,6 +67,7 @@ static const char *extendsElementC = "extends"; static const char *addPageMethodC = "addpagemethod"; static const char *propertySpecsC = "propertyspecifications"; static const char *stringPropertySpecC = "stringpropertyspecification"; +static const char propertyToolTipC[] = "tooltip"; static const char *stringPropertyNameAttrC = "name"; static const char *stringPropertyTypeAttrC = "type"; static const char *stringPropertyNoTrAttrC = "notr"; @@ -142,6 +143,7 @@ public: // Type of a string property typedef QPair StringPropertyType; typedef QHash StringPropertyTypeMap; + typedef QHash PropertyToolTipMap; explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {} void clearXML(); @@ -155,6 +157,7 @@ public: QString xmlExtends; StringPropertyTypeMap xmlStringPropertyTypeMap; + PropertyToolTipMap propertyToolTipMap; }; void QDesignerCustomWidgetSharedData::clearXML() @@ -235,6 +238,11 @@ bool QDesignerCustomWidgetData::xmlStringPropertyType(const QString &name, Strin return true; } +QString QDesignerCustomWidgetData::propertyToolTip(const QString &name) const +{ + return m_d->propertyToolTipMap.value(name); +} + // Wind a QXmlStreamReader until it finds an element. Returns index or one of FindResult enum FindResult { FindError = -2, ElementNotFound = -1 }; @@ -290,12 +298,13 @@ static qdesigner_internal::TextPropertyValidationMode typeStringToType(const QSt return qdesigner_internal::ValidationRichText; } -static bool parsePropertySpecs(QXmlStreamReader &sr, - QDesignerCustomWidgetSharedData::StringPropertyTypeMap *rc, - QString *errorMessage) +static bool parsePropertySpecs(QXmlStreamReader &sr, + QDesignerCustomWidgetSharedData *data, + QString *errorMessage) { const QString propertySpecs = QLatin1String(propertySpecsC); const QString stringPropertySpec = QLatin1String(stringPropertySpecC); + const QString propertyToolTip = QLatin1String(propertyToolTipC); const QString stringPropertyTypeAttr = QLatin1String(stringPropertyTypeAttrC); const QString stringPropertyNoTrAttr = QLatin1String(stringPropertyNoTrAttrC); const QString stringPropertyNameAttr = QLatin1String(stringPropertyNameAttrC); @@ -303,31 +312,39 @@ static bool parsePropertySpecs(QXmlStreamReader &sr, while (!sr.atEnd()) { switch(sr.readNext()) { case QXmlStreamReader::StartElement: { - if (sr.name() != stringPropertySpec) { + if (sr.name() == stringPropertySpec) { + const QXmlStreamAttributes atts = sr.attributes(); + const QString name = atts.value(stringPropertyNameAttr).toString(); + const QString type = atts.value(stringPropertyTypeAttr).toString(); + const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional + + if (type.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyTypeAttr); + return false; + } + if (name.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyNameAttr); + return false; + } + bool typeOk; + const bool noTr = notrS == QStringLiteral("true") || notrS == QStringLiteral("1"); + QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr); + if (!typeOk) { + *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification.").arg(type); + return false; + } + data->xmlStringPropertyTypeMap.insert(name, v); + } else if (sr.name() == propertyToolTip) { + const QString name = sr.attributes().value(stringPropertyNameAttr).toString(); + if (name.isEmpty()) { + *errorMessage = msgAttributeMissing(stringPropertyNameAttr); + return false; + } + data->propertyToolTipMap.insert(name, sr.readElementText().trimmed()); + } else { *errorMessage = QDesignerPluginManager::tr("An invalid property specification ('%1') was encountered. Supported types: %2").arg(sr.name().toString(), stringPropertySpec); return false; } - const QXmlStreamAttributes atts = sr.attributes(); - const QString name = atts.value(stringPropertyNameAttr).toString(); - const QString type = atts.value(stringPropertyTypeAttr).toString(); - const QString notrS = atts.value(stringPropertyNoTrAttr).toString(); //Optional - - if (type.isEmpty()) { - *errorMessage = msgAttributeMissing(stringPropertyTypeAttr); - return false; - } - if (name.isEmpty()) { - *errorMessage = msgAttributeMissing(stringPropertyNameAttr); - return false; - } - bool typeOk; - const bool noTr = notrS == QStringLiteral("true") || notrS == QStringLiteral("1"); - QDesignerCustomWidgetSharedData::StringPropertyType v(typeStringToType(type, &typeOk), !noTr); - if (!typeOk) { - *errorMessage = QDesignerPluginManager::tr("'%1' is not a valid string property specification.").arg(type); - return false; - } - rc->insert(name, v); } break; case QXmlStreamReader::EndElement: // Outer @@ -429,7 +446,7 @@ QDesignerCustomWidgetData::ParseResult } break; case 2: // - if (!parsePropertySpecs(sr, &m_d->xmlStringPropertyTypeMap, errorMessage)) { + if (!parsePropertySpecs(sr, m_d.data(), errorMessage)) { *errorMessage = msgXmlError(name, *errorMessage); return ParseError; } diff --git a/src/designer/src/lib/shared/pluginmanager_p.h b/src/designer/src/lib/shared/pluginmanager_p.h index af1734767..08d6f4cd7 100644 --- a/src/designer/src/lib/shared/pluginmanager_p.h +++ b/src/designer/src/lib/shared/pluginmanager_p.h @@ -93,6 +93,8 @@ public: QString xmlDisplayName() const; // Type of a string property bool xmlStringPropertyType(const QString &name, StringPropertyType *type) const; + // Custom tool tip of property + QString propertyToolTip(const QString &name) const; private: QSharedDataPointer m_d; diff --git a/src/designer/src/lib/uilib/ui4.cpp b/src/designer/src/lib/uilib/ui4.cpp index 46354922a..96c91afe9 100644 --- a/src/designer/src/lib/uilib/ui4.cpp +++ b/src/designer/src/lib/uilib/ui4.cpp @@ -8776,6 +8776,8 @@ void DomSlots::setElementSlot(const QStringList& a) void DomPropertySpecifications::clear(bool clear_all) { + qDeleteAll(m_tooltip); + m_tooltip.clear(); qDeleteAll(m_stringpropertyspecification); m_stringpropertyspecification.clear(); @@ -8793,6 +8795,8 @@ DomPropertySpecifications::DomPropertySpecifications() DomPropertySpecifications::~DomPropertySpecifications() { + qDeleteAll(m_tooltip); + m_tooltip.clear(); qDeleteAll(m_stringpropertyspecification); m_stringpropertyspecification.clear(); } @@ -8804,6 +8808,12 @@ void DomPropertySpecifications::read(QXmlStreamReader &reader) switch (reader.readNext()) { case QXmlStreamReader::StartElement : { const QString tag = reader.name().toString().toLower(); + if (tag == QLatin1String("tooltip")) { + DomPropertyToolTip *v = new DomPropertyToolTip(); + v->read(reader); + m_tooltip.append(v); + continue; + } if (tag == QLatin1String("stringpropertyspecification")) { DomStringPropertySpecification *v = new DomStringPropertySpecification(); v->read(reader); @@ -8830,6 +8840,10 @@ void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &t { writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertyspecifications") : tagName.toLower()); + for (int i = 0; i < m_tooltip.size(); ++i) { + DomPropertyToolTip* v = m_tooltip[i]; + v->write(writer, QStringLiteral("tooltip")); + } for (int i = 0; i < m_stringpropertyspecification.size(); ++i) { DomStringPropertySpecification* v = m_stringpropertyspecification[i]; v->write(writer, QStringLiteral("stringpropertyspecification")); @@ -8840,12 +8854,84 @@ void DomPropertySpecifications::write(QXmlStreamWriter &writer, const QString &t writer.writeEndElement(); } +void DomPropertySpecifications::setElementTooltip(const QList& a) +{ + m_children |= Tooltip; + m_tooltip = a; +} + void DomPropertySpecifications::setElementStringpropertyspecification(const QList& a) { m_children |= Stringpropertyspecification; m_stringpropertyspecification = a; } +void DomPropertyToolTip::clear(bool clear_all) +{ + + if (clear_all) { + m_text.clear(); + m_has_attr_name = false; + } + + m_children = 0; +} + +DomPropertyToolTip::DomPropertyToolTip() +{ + m_children = 0; + m_has_attr_name = false; +} + +DomPropertyToolTip::~DomPropertyToolTip() +{ +} + +void DomPropertyToolTip::read(QXmlStreamReader &reader) +{ + + foreach (const QXmlStreamAttribute &attribute, reader.attributes()) { + QStringRef name = attribute.name(); + if (name == QLatin1String("name")) { + setAttributeName(attribute.value().toString()); + continue; + } + reader.raiseError(QStringLiteral("Unexpected attribute ") + name.toString()); + } + + for (bool finished = false; !finished && !reader.hasError();) { + switch (reader.readNext()) { + case QXmlStreamReader::StartElement : { + const QString tag = reader.name().toString().toLower(); + reader.raiseError(QStringLiteral("Unexpected element ") + tag); + } + break; + case QXmlStreamReader::EndElement : + finished = true; + break; + case QXmlStreamReader::Characters : + if (!reader.isWhitespace()) + m_text.append(reader.text().toString()); + break; + default : + break; + } + } +} + +void DomPropertyToolTip::write(QXmlStreamWriter &writer, const QString &tagName) const +{ + writer.writeStartElement(tagName.isEmpty() ? QString::fromUtf8("propertytooltip") : tagName.toLower()); + + if (hasAttributeName()) + writer.writeAttribute(QStringLiteral("name"), attributeName()); + + if (!m_text.isEmpty()) + writer.writeCharacters(m_text); + + writer.writeEndElement(); +} + void DomStringPropertySpecification::clear(bool clear_all) { diff --git a/src/designer/src/lib/uilib/ui4_p.h b/src/designer/src/lib/uilib/ui4_p.h index 60685c911..1e5bcbf0a 100644 --- a/src/designer/src/lib/uilib/ui4_p.h +++ b/src/designer/src/lib/uilib/ui4_p.h @@ -144,6 +144,7 @@ class DomWidgetData; class DomDesignerData; class DomSlots; class DomPropertySpecifications; +class DomPropertyToolTip; class DomStringPropertySpecification; /******************************************************************************* @@ -3541,6 +3542,9 @@ public: // attribute accessors // child element accessors + inline QList elementTooltip() const { return m_tooltip; } + void setElementTooltip(const QList& a); + inline QList elementStringpropertyspecification() const { return m_stringpropertyspecification; } void setElementStringpropertyspecification(const QList& a); @@ -3551,15 +3555,49 @@ private: // attribute data // child element data uint m_children; + QList m_tooltip; QList m_stringpropertyspecification; enum Child { - Stringpropertyspecification = 1 + Tooltip = 1, + Stringpropertyspecification = 2 }; DomPropertySpecifications(const DomPropertySpecifications &other); void operator = (const DomPropertySpecifications&other); }; +class QDESIGNER_UILIB_EXPORT DomPropertyToolTip { +public: + DomPropertyToolTip(); + ~DomPropertyToolTip(); + + void read(QXmlStreamReader &reader); + void write(QXmlStreamWriter &writer, const QString &tagName = QString()) const; + inline QString text() const { return m_text; } + inline void setText(const QString &s) { m_text = s; } + + // attribute accessors + inline bool hasAttributeName() const { return m_has_attr_name; } + inline QString attributeName() const { return m_attr_name; } + inline void setAttributeName(const QString& a) { m_attr_name = a; m_has_attr_name = true; } + inline void clearAttributeName() { m_has_attr_name = false; } + + // child element accessors +private: + QString m_text; + void clear(bool clear_all = true); + + // attribute data + QString m_attr_name; + bool m_has_attr_name; + + // child element data + uint m_children; + + DomPropertyToolTip(const DomPropertyToolTip &other); + void operator = (const DomPropertyToolTip&other); +}; + class QDESIGNER_UILIB_EXPORT DomStringPropertySpecification { public: DomStringPropertySpecification(); -- cgit v1.2.3