diff --git a/Core/PropertyBase.cpp b/Core/PropertyBase.cpp index 47f1eaf9..59e2dcb6 100644 --- a/Core/PropertyBase.cpp +++ b/Core/PropertyBase.cpp @@ -18,35 +18,13 @@ #include static int qtnPropertyChangeReasonMetaId = qRegisterMetaType("QtnPropertyChangeReason"); -static int qtnPropertyStateMetaId = qRegisterMetaType("QtnPropertyState"); +static int qtnPropertyPtrId = qRegisterMetaType("QtnPropertyBase*"); static quint16 qtnPropertyMagicNumber = 0x1984; // extern declaration void qtnAddPropertyAsChild(QObject* parent, QtnPropertyBase* child, bool moveOwnership); void qtnRemovePropertyAsChild(QObject* parent, QtnPropertyBase* child); -static QScriptValue qtnPropertyIdToScriptValue(QScriptEngine* engine, const QtnPropertyID& val) -{ - QScriptValue obj((int)val); - return obj; -} - -static void qtnPropertyIdFromScriptValue(const QScriptValue& obj, QtnPropertyID& val) -{ - val = obj.toInt32(); -} - -static QScriptValue qtnPropertyStateToScriptValue(QScriptEngine* engine, const QtnPropertyState& val) -{ - QScriptValue obj((QtnPropertyState::Int)val); - return obj; -} - -static void qtnPropertyStateFromScriptValue(const QScriptValue& obj, QtnPropertyState& val) -{ - val = (QtnPropertyState::enum_type)obj.toInt32(); -} - static QScriptValue qtnPropertyChangeReasonToScriptValue(QScriptEngine* engine, const QtnPropertyChangeReason& val) { QScriptValue obj((QtnPropertyChangeReason::Int)val); @@ -85,8 +63,6 @@ static void qtnPropertyBasePtrFromScriptValue(const QScriptValue& obj, QtnProper void qtnScriptRegisterPropertyTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, qtnPropertyIdToScriptValue, qtnPropertyIdFromScriptValue); - qScriptRegisterMetaType(engine, qtnPropertyStateToScriptValue, qtnPropertyStateFromScriptValue); qScriptRegisterMetaType(engine, qtnPropertyChangeReasonToScriptValue, qtnPropertyChangeReasonFromScriptValue); qScriptRegisterMetaType(engine, qtnPropertyValuePtrToScriptValue, qtnPropertyValuePtrFromScriptValue); qScriptRegisterMetaType(engine, qtnPropertyBasePtrToScriptValue, qtnPropertyBasePtrFromScriptValue); diff --git a/Core/PropertyBase.h b/Core/PropertyBase.h index dfb9413b..9da21013 100644 --- a/Core/PropertyBase.h +++ b/Core/PropertyBase.h @@ -82,9 +82,9 @@ class QTN_PE_CORE_EXPORT QtnPropertyBase: public QObject public: // properties for scripting Q_PROPERTY(QString name READ name) Q_PROPERTY(QString description READ description) - Q_PROPERTY(QtnPropertyID id READ id) + Q_PROPERTY(qint32 id READ id) Q_PROPERTY(bool isEditable READ isEditableByUser) - Q_PROPERTY(QtnPropertyState state READ state) + Q_PROPERTY(quint32 state READ state) Q_PROPERTY(QVariant value READ valueAsVariant WRITE setValueAsVariant) Q_SIGNALS: @@ -132,5 +132,6 @@ QTN_PE_CORE_EXPORT QDataStream& operator>> (QDataStream& stream, QtnPropertyBase QTN_PE_CORE_EXPORT void qtnScriptRegisterPropertyTypes(QScriptEngine* engine); Q_DECLARE_METATYPE(const QtnPropertyBase*) +Q_DECLARE_METATYPE(QtnPropertyBase*) #endif // QTN_PROPERTY_BASE_H diff --git a/Core/PropertySet.cpp b/Core/PropertySet.cpp index 3872fd35..deeba85c 100644 --- a/Core/PropertySet.cpp +++ b/Core/PropertySet.cpp @@ -17,6 +17,9 @@ #include "PropertySet.h" #include +static int qtnPropertySetPtrId = qRegisterMetaType("QtnPropertySet*"); + + void qtnAddPropertyAsChild(QObject *parent, QtnPropertyBase* child, bool moveOwnership) { QtnPropertySet* propertySet = qobject_cast(parent); diff --git a/Core/PropertySet.h b/Core/PropertySet.h index d76e4b78..63881236 100644 --- a/Core/PropertySet.h +++ b/Core/PropertySet.h @@ -28,6 +28,7 @@ class QTN_PE_CORE_EXPORT QtnPropertySet: public QtnPropertyBase explicit QtnPropertySet(QObject* parent); virtual ~QtnPropertySet(); +public slots: // sub properties bool hasChildProperties() const { return !m_childProperties.empty(); } const QList& childProperties() const { return m_childProperties; } @@ -85,4 +86,6 @@ class QTN_PE_CORE_EXPORT QtnPropertySet: public QtnPropertyBase friend void qtnDisconnectChildProperty(QtnPropertySet* masterProperty, QtnPropertyBase* childProperty); }; +Q_DECLARE_METATYPE(QtnPropertySet*) + #endif // QTN_PROPERTY_SET_H diff --git a/Demo/MainWindow.cpp b/Demo/MainWindow.cpp index 7b3333ee..24952206 100644 --- a/Demo/MainWindow.cpp +++ b/Demo/MainWindow.cpp @@ -2,6 +2,7 @@ #include "ui_MainWindow.h" #include "mydialog.h" #include +#include #include "QObjectPropertySet.h" #include "Demo.peg.h" @@ -21,6 +22,8 @@ MainWindow::MainWindow(QWidget *parent) : jsEngine.globalObject().setProperty("samplePS", jsEngine.newQObject(ps)); dbg.attachTo(&jsEngine); + + move(QApplication::desktop()->availableGeometry().center() - rect().center()); } MainWindow::~MainWindow() diff --git a/Demo/MainWindow.ui b/Demo/MainWindow.ui index 2af3eaa0..b8031ee7 100644 --- a/Demo/MainWindow.ui +++ b/Demo/MainWindow.ui @@ -10,7 +10,7 @@ 0 0 742 - 417 + 468 @@ -51,7 +51,7 @@ - Edit in modal dialog + Edit in modal dialog... diff --git a/PropertyWidget/Delegates/PropertyDelegate.cpp b/PropertyWidget/Delegates/PropertyDelegate.cpp index d7e064f0..edafaaa0 100644 --- a/PropertyWidget/Delegates/PropertyDelegate.cpp +++ b/PropertyWidget/Delegates/PropertyDelegate.cpp @@ -61,7 +61,12 @@ bool QtnPropertyDelegate::acceptKeyPressedForInplaceEdit(QKeyEvent* keyEvent) co QWidget* QtnPropertyDelegate::createValueEditor(QWidget* parent, const QRect& rect, QtnInplaceInfo* inplaceInfo) { - return createValueEditorImpl(parent, rect, inplaceInfo); + QWidget* valueEditor = createValueEditorImpl(parent, rect, inplaceInfo); + if (!valueEditor) + return valueEditor; + + valueEditor->setObjectName("QtnPropertyValueEditor"); + return valueEditor; } void QtnPropertyDelegate::drawValueImpl(QStylePainter& painter, const QRect& rect, const QStyle::State& state, bool* needTooltip) const diff --git a/PropertyWidget/PropertyView.cpp b/PropertyWidget/PropertyView.cpp index c115050d..bfdd02c1 100644 --- a/PropertyWidget/PropertyView.cpp +++ b/PropertyWidget/PropertyView.cpp @@ -79,7 +79,8 @@ QtnPropertyView::QtnPropertyView(QWidget* parent, QtnPropertySet* propertySet) m_itemHeightSpacing(6), m_leadMargin(0), m_splitRatio(0.5f), - m_rubberBand(nullptr) + m_rubberBand(nullptr), + m_accessibilityProxy(nullptr) { setFocusPolicy(Qt::StrongFocus); viewport()->setMouseTracking(true); @@ -93,6 +94,15 @@ QtnPropertyView::~QtnPropertyView() { } +QtnAccessibilityProxy *QtnPropertyView::accessibilityProxy() +{ + if (!m_accessibilityProxy) + m_accessibilityProxy = new QtnAccessibilityProxy(this); + + return m_accessibilityProxy; +} + + void QtnPropertyView::setPropertySet(QtnPropertySet* newPropertySet) { if (m_propertySet) diff --git a/PropertyWidget/PropertyView.h b/PropertyWidget/PropertyView.h index bf6a1243..eed2bf6a 100644 --- a/PropertyWidget/PropertyView.h +++ b/PropertyWidget/PropertyView.h @@ -20,6 +20,7 @@ #include "PropertyWidgetAPI.h" #include "Delegates/PropertyDelegateFactory.h" #include "../Core/PropertySet.h" +#include "Utils/AccessibilityProxy.h" #include #include @@ -77,6 +78,9 @@ class QTN_PW_EXPORT QtnPropertyView: public QAbstractScrollArea void addPropertyViewStyle(QtnPropertyViewStyle style); void removePropertyViewStyle(QtnPropertyViewStyle style); +public slots: + QtnAccessibilityProxy* accessibilityProxy(); + Q_SIGNALS: // emits when active property has changed void activePropertyChanged(QtnPropertyBase* activeProperty); @@ -190,6 +194,9 @@ class QTN_PW_EXPORT QtnPropertyView: public QAbstractScrollArea float m_splitRatio; QRubberBand* m_rubberBand; + + friend class QtnAccessibilityProxy; + QtnAccessibilityProxy* m_accessibilityProxy; }; #endif // QTN_PROPERTYVIEW_H diff --git a/PropertyWidget/PropertyWidget.pro b/PropertyWidget/PropertyWidget.pro index b21f340f..ee1d8bfc 100644 --- a/PropertyWidget/PropertyWidget.pro +++ b/PropertyWidget/PropertyWidget.pro @@ -27,7 +27,8 @@ SOURCES += PropertyWidget.cpp \ Delegates/PropertyEditorAux.cpp \ Delegates/Core/PropertyDelegateQSize.cpp \ Delegates/Core/PropertyDelegateQPoint.cpp \ - Delegates/GUI/PropertyDelegateQFont.cpp + Delegates/GUI/PropertyDelegateQFont.cpp \ + Utils/AccessibilityProxy.cpp HEADERS += PropertyWidgetAPI.h \ PropertyWidget.h \ @@ -49,7 +50,8 @@ HEADERS += PropertyWidgetAPI.h \ Delegates/PropertyEditorAux.h \ Delegates/Core/PropertyDelegateQSize.h \ Delegates/Core/PropertyDelegateQPoint.h \ - Delegates/GUI/PropertyDelegateQFont.h + Delegates/GUI/PropertyDelegateQFont.h \ + Utils/AccessibilityProxy.h LIBS += -L$$BIN_DIR -lQtnPropertyCore diff --git a/PropertyWidget/Utils/AccessibilityProxy.cpp b/PropertyWidget/Utils/AccessibilityProxy.cpp new file mode 100644 index 00000000..81f32828 --- /dev/null +++ b/PropertyWidget/Utils/AccessibilityProxy.cpp @@ -0,0 +1,146 @@ +/* + Copyright (c) 2012-1015 Alex Zhondin + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "AccessibilityProxy.h" +#include "../PropertyView.h" +#include + +static int typeID = qRegisterMetaType("QtnAccessibilityProxy*"); + +QtnAccessibilityProxy::QtnAccessibilityProxy(QtnPropertyView* owner) + : QObject(owner), + m_owner(owner) +{ +} + +QtnAccessibilityProxy::~QtnAccessibilityProxy() +{ +} + +QtnPropertyView* QtnAccessibilityProxy::owner() +{ + return m_owner; +} + +QtnPropertySet* QtnAccessibilityProxy::propertySet() +{ + return m_owner->propertySet(); +} + +QtnPropertyBase* QtnAccessibilityProxy::activeProperty() +{ + return m_owner->activeProperty(); +} + +QtnPropertyBase* QtnAccessibilityProxy::findProperty(QString nameOrPath) +{ + auto root = m_owner->propertySet(); + if (!root) + return nullptr; + + auto properties = root->findChildProperties(nameOrPath); + if (properties.size() != 1) + return nullptr; + + return properties[0]; +} + +QtnPropertyBase* QtnAccessibilityProxy::propertyUnderPoint(QPoint point) +{ + int index = m_owner->visibleItemIndexByPoint(point); + if (index < 0) + return nullptr; + + return m_owner->m_visibleItems[index].item->property; +} + +void QtnAccessibilityProxy::ensureVisibleProperty(QtnPropertyBase* property) +{ + if (!property) + return; + + auto propertySet = qobject_cast(property->parent()); + while (propertySet) + { + propertySet->removeState(QtnPropertyStateCollapsed); + propertySet = qobject_cast(propertySet->parent()); + } + + m_owner->ensureVisible(property); +} + +QRect QtnAccessibilityProxy::propertyNameRect(QtnPropertyBase* property) +{ + if (!property) + return QRect(); + + int index = m_owner->visibleItemIndexByProperty(property); + if (index < 0) + return QRect(); + + QRect rect = m_owner->visibleItemRect(index); + rect.setRight(m_owner->splitPosition()); + return rect; +} + +QRect QtnAccessibilityProxy::propertyValueRect(QtnPropertyBase* property) +{ + if (!property) + return QRect(); + + int index = m_owner->visibleItemIndexByProperty(property); + if (index < 0) + return QRect(); + + QRect rect = m_owner->visibleItemRect(index); + rect.setLeft(m_owner->splitPosition() + 1); + return rect; +} + +QRect QtnAccessibilityProxy::propertyActionRect(QtnPropertyBase* property, int actionIndex) +{ + if (!property) + return QRect(); + + int index = m_owner->visibleItemIndexByProperty(property); + if (index < 0) + return QRect(); + + const auto& item = m_owner->m_visibleItems[index]; + if (!item.actionsValid) + return QRect(); + + if (actionIndex < 0 || actionIndex >= item.actions.size()) + return QRect(); + + return item.actions[actionIndex].rect; +} + +QString QtnAccessibilityProxy::propertyDelegateName(QtnPropertyBase* property) +{ + if (!property) + return QString(); + + auto theProperty = property->asProperty(); + if (!theProperty) + return QString(); + + if (!theProperty->delegate()) + return QString("default"); + + return theProperty->delegate()->name; +} + diff --git a/PropertyWidget/Utils/AccessibilityProxy.h b/PropertyWidget/Utils/AccessibilityProxy.h new file mode 100644 index 00000000..5d6d27f5 --- /dev/null +++ b/PropertyWidget/Utils/AccessibilityProxy.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2012-1015 Alex Zhondin + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef QTN_ACCESSIBILITY_PROXY_H +#define QTN_ACCESSIBILITY_PROXY_H + +#include "PropertyWidgetAPI.h" +#include "../Core/PropertySet.h" +#include +#include + +class QtnPropertyView; + +class QTN_PW_EXPORT QtnAccessibilityProxy: public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QtnAccessibilityProxy) + +public: + explicit QtnAccessibilityProxy(QtnPropertyView* owner = 0); + ~QtnAccessibilityProxy(); + +public slots: + QtnPropertyView* owner(); + QtnPropertyBase* activeProperty(); + QtnPropertySet* propertySet(); + QtnPropertyBase* findProperty(QString nameOrPath); + QtnPropertyBase* propertyUnderPoint(QPoint point); + void ensureVisibleProperty(QtnPropertyBase* property); + + QRect propertyNameRect(QtnPropertyBase* property); + QRect propertyValueRect(QtnPropertyBase* property); + QRect propertyActionRect(QtnPropertyBase* property, int actionIndex); + + QString propertyDelegateName(QtnPropertyBase* property); + +private: + QtnPropertyView* m_owner; +}; + +Q_DECLARE_METATYPE(QtnAccessibilityProxy*) + + +#endif // QTN_ACCESSIBILITY_PROXY_H diff --git a/Tests/TestProperty.cpp b/Tests/TestProperty.cpp index 7747b276..03e6a7e7 100644 --- a/Tests/TestProperty.cpp +++ b/Tests/TestProperty.cpp @@ -794,6 +794,15 @@ void TestProperty::propertyScripting() val = eng.evaluate("b.isEditable"); QCOMPARE(val.toBool(), false); + val = eng.evaluate("b.state"); + QCOMPARE(val.toUInt32(), 2u); + + val = eng.evaluate("b.state == QtnPropertyStateInvisible"); + QCOMPARE(val.toBool(), true); + + val = eng.evaluate("b.state == QtnPropertyStateNonSerialized"); + QCOMPARE(val.toBool(), false); + val = eng.evaluate("b.value"); QCOMPARE(val.toBool(), false); @@ -804,6 +813,9 @@ void TestProperty::propertyScripting() val = eng.evaluate("b.value = false"); QCOMPARE(b.value(), true); + eng.evaluate("b.state = 0"); + QCOMPARE(b.state(), QtnPropertyStateInvisible); + b.setState(QtnPropertyStateNone); val = eng.evaluate("b.value = false"); QCOMPARE(b.value(), false); diff --git a/Tests/suite_squish_test_suit/envvars b/Tests/suite_squish_test_suit/envvars new file mode 100644 index 00000000..e69de29b diff --git a/Tests/suite_squish_test_suit/objects.map b/Tests/suite_squish_test_suit/objects.map new file mode 100644 index 00000000..f4b5c629 --- /dev/null +++ b/Tests/suite_squish_test_suit/objects.map @@ -0,0 +1,4 @@ +:PropertyWidget Demo.QtnPropertyValueEditor_QComboBox {name='QtnPropertyValueEditor' type='QComboBox' visible='1' window=':PropertyWidget Demo_MainWindow'} +:PropertyWidget Demo_MainWindow {name='MainWindow' type='MainWindow' visible='1' windowTitle='PropertyWidget Demo'} +:PropertyWidget Demo_QtnPropertyView {type='QtnPropertyView' unnamed='1' visible='1' window=':PropertyWidget Demo_MainWindow'} +:unnamed unnamed diff --git a/Tests/suite_squish_test_suit/suite.conf b/Tests/suite_squish_test_suit/suite.conf new file mode 100644 index 00000000..c659487b --- /dev/null +++ b/Tests/suite_squish_test_suit/suite.conf @@ -0,0 +1,9 @@ +AUT=QtnPropertyDemo +CWD= +ENVVARS=envvars +HOOK_SUB_PROCESSES=false +IMPLICITAUTSTART=1 +LANGUAGE=JavaScript +TEST_CASES=tst_gui_test +VERSION=3 +WRAPPERS=Qt diff --git a/Tests/suite_squish_test_suit/tst_gui_test/test.js b/Tests/suite_squish_test_suit/tst_gui_test/test.js new file mode 100644 index 00000000..faa59290 --- /dev/null +++ b/Tests/suite_squish_test_suit/tst_gui_test/test.js @@ -0,0 +1,112 @@ +function waitForChild() { + if (arguments.length < 2) + return null + var obj = arguments[0] + objectMap.add(obj) + obj = waitForObject("{type='"+arguments[1]+"' parent='"+objectMap.symbolicName(obj)+"'}") + for (var i = 2; i < arguments.length; i++) { + objectMap.add(obj) + obj = waitForObject("{type='"+arguments[i]+"' parent='"+objectMap.symbolicName(obj)+"'}") + } + return obj +} + +function main() { + var w = waitForObject("{type='QtnPropertyView' visible='1' window=':PropertyWidget Demo_MainWindow'}") + var ap = w.accessibilityProxy() + + // test BoolProperty + var p = ap.findProperty("BoolProperty") + test.verify(!isNull(p), "BoolProperty should exists") + // check property value + test.compare(object.convertTo(p.value,"bool"), false) + // check property delegate name + test.compare(ap.propertyDelegateName(p), "default") + // check property description + test.compare(p.description, "Property to hold boolean values.") + // get center of property edit rect + var pt = ap.propertyActionRect(p, 0).center() + mouseClick(w, pt.x, pt.y, Qt.NoModifier, Qt.LeftButton) + snooze(1) + // wait for popup checkbox widget + var e = waitForChild(w, "QCheckBox") + test.compare(e.checked, false) + // modify property + pt = e.rect.center() + mouseClick(e, pt.x, pt.y, Qt.NoModifier, Qt.LeftButton) + type(e, "") + test.compare(object.convertTo(p.value,"bool"), true) + snooze(1) + + // enable subproperty set + p = ap.findProperty("EnableSubPropertySet") + test.verify(!isNull(p)) + // get center of property name rect + pt = ap.propertyNameRect(p).center() + // activate property + mouseClick(w, pt.x, pt.y, Qt.NoModifier, Qt.LeftButton) + snooze(1) + var p2 = ap.activeProperty() + test.compare(p, p2, "EnableSubPropertySet should become active") + + // verify SubPropertySet is disabled + p2 = ap.findProperty("SubPropertySet") + test.verify(!isNull(p2)) + test.compare(p2.isEditable, false) + test.compare(p2.state, 4) // QtnPropertyStateImmutable + // start edit + type(w, "") + snooze(1) + // wait for popup checkbox widget + e = waitForChild(w, "QCheckBox") + type(e, " ") + type(e, "") + // now SubPropertySet should be enabled + test.compare(p2.isEditable, true) + test.compare(p2.state, 0) + + // navigation by keyboard + snooze(1) + type(w, "") + test.compare(ap.activeProperty().name, "SubPropertySet") + snooze(1) + // collapse property set by keyboard + type(w, "") + p = ap.findProperty("SubPropertySet.ReadOnlyString") + test.verify(!isNull(p)) + test.compare(p.isEditable, false) + test.compare(p.state, 12) // QtnPropertyStateImmutable | QtnPropertyStateCollapsed + snooze(1) + + // expand property set by mouse + pt = ap.propertyActionRect(ap.findProperty("SubPropertySet"), 0).center() + mouseClick(w, pt.x, pt.y, Qt.NoModifier, Qt.LeftButton) + test.compare(p.state, 4) // QtnPropertyStateImmutable + snooze(1) + + //test string list delegate + p = ap.findProperty("SubPropertySet.StringFromList") + pt = ap.propertyActionRect(p, 0).center() + mouseClick(w, pt.x, pt.y, Qt.NoModifier, Qt.LeftButton) + e = waitForChild(w, "QComboBox") + // get popup listbox + e = e.view() + snooze(1) + clickItem(e, "four", 0, 0, Qt.NoModifier, Qt.LeftButton) + // check property value + test.compare(object.convertTo(p.value,"QString"), "four") + snooze(1) + + p = ap.findProperty("SubPropertySet2.FileNameProperty") + ap.ensureVisibleProperty(p) + p = ap.propertyActionRect(p, 0).center() + mouseClick(w, p.x, p.y, Qt.NoModifier, Qt.LeftButton) + snooze(1) + + e = waitForChild(w, "QLineEdit") + type(e, "hello from squish") + type(e, "") + snooze(3) + + return +} \ No newline at end of file