diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2012-04-11 14:56:22 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@nokia.com> | 2012-04-11 16:05:03 +0200 |
commit | a896d4b39ec3d45ba708d9b36ea9c864b1df2136 (patch) | |
tree | 45cfe153cce6114c2c76c48dc0bdabde2a8cf3e3 | |
parent | 24fb8dc27eddfdd62bd2c3a6e863cbf433762cd6 (diff) | |
parent | 65bfc35429e845cf6b76d58107360a1360a654fc (diff) |
Merge remote-tracking branch 'origin/master' into api_changes
Conflicts:
src/qml/debugger/qqmlprofilerservice_p.h
src/qml/qml/qqmlboundsignal.cpp
src/qml/qml/v4/qv4bindings.cpp
src/quick/items/qquickshadereffect.cpp
src/quick/particles/qquickcustomparticle.cpp
src/quick/qtquick2.cpp
Change-Id: Ia9c6517035ae912fa75e77473a452bd3383def56
235 files changed, 5468 insertions, 4208 deletions
diff --git a/doc/src/examples/example-textballoons.qdoc b/doc/src/examples/example-textballoons.qdoc index be004f59ed..299ceeb808 100644 --- a/doc/src/examples/example-textballoons.qdoc +++ b/doc/src/examples/example-textballoons.qdoc @@ -28,7 +28,7 @@ /*! \title Scenegraph Painted Item Example - \example declarative/painteditem/textballoons + \example quick/painteditem/textballoons The Painted Item example shows how to use the QML Scene Graph framework to implement custom scenegraph items using QPainter. diff --git a/doc/src/examples/examples-toys.qdoc b/doc/src/examples/examples-toys.qdoc index 7414579e19..87d2ad58f6 100644 --- a/doc/src/examples/examples-toys.qdoc +++ b/doc/src/examples/examples-toys.qdoc @@ -47,16 +47,3 @@ \image qml-corkboards-example.png */ -/*! - \title QML Example - Dynamic Scene - \example declarative/toys/dynamicscene - \brief This example demonstrates creating components dynamically. - \image qml-dynamicscene-example.png -*/ - -/*! - \title QML Example - Clocks - \example declarative/toys/clocks - \brief This example demonstrates creating components and using them multiple times. - \image qml-clocks-example.png -*/ diff --git a/doc/src/examples/examples.qdoc b/doc/src/examples/examples.qdoc index a99dc8c263..b5cd036c03 100644 --- a/doc/src/examples/examples.qdoc +++ b/doc/src/examples/examples.qdoc @@ -51,39 +51,39 @@ This set of code samples are part of the collection of \l{Qt Examples}. Qt Quick Applications \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/calculator}{Calculator} + \l{demos/calculator}{Calculator} \image qml-calculator-example-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/flickr}{Flickr Mobile} + \l{demos/flickr}{Flickr Mobile} \image qml-flickr-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/minehunt}{Minehunt} + \l{demos/minehunt}{Minehunt} \image qml-minehunt-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/photoviewer}{Photo Viewer} + \l{demos/photoviewer}{Photo Viewer} \image qml-photoviewer-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/rssnews}{RSS News Reader} + \l{demos/rssnews}{RSS News Reader} \image qml-rssnews-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/samegame}{Same Game} + \l{demos/samegame}{Same Game} \image qml-samegame-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/snake}{Snake} + \l{demos/snake}{Snake} \image qml-snake-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/twitter}{Twitter} + \l{demos/twitter}{Twitter} \image qml-twitter-demo-small.png \enddiv \div {class="threecolumn_piece"} - \l{demos/declarative/webbrowser}{Web Browser} + \l{demos/webbrowser}{Web Browser} \image qml-webbrowser-demo-small.png \enddiv \enddiv @@ -93,132 +93,19 @@ This set of code samples are part of the collection of \l{Qt Examples}. \enddiv Code samples demonstrate a general use for QML features. Some showcase how elements or properties can be used in an application. - \div {class="threecolumn_piece"} - \div {class="heading"} - QML Features - \enddiv - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Mouse and Keyboard Input - \enddiv - \list - \li \l{declarative/text/fonts}{Fonts} - \li \l{declarative/text/textselection}{Text Selection} - \li \l{declarative/keyinteraction/focus}{Keyboard Focus} - \li \l{declarative/touchinteraction/mousearea}{MouseArea} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - States and Transitions - \enddiv - \list - \li \l{declarative/animation/states}{States} - \li \l{declarative/animation/basics}{Animation Essentials} - \li \l{declarative/animation/behaviors}{Behaviors} - \li \l{declarative/animation/easing}{Easing} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - UI Components - \enddiv - \list - \li \l{declarative/ui-components/dialcontrol}{Dial Control} - \li \l{declarative/ui-components/flipable}{Flipable} - \li \l{declarative/ui-components/progressbar}{Progress Bar} - \li \l{declarative/ui-components/scrollbar}{Scroll Bar} - \li \l{declarative/ui-components/searchbox}{Search Box} - \li \l{declarative/ui-components/slideswitch}{Slide Switch} - \li \l{declarative/ui-components/spinner}{Spinner} - \li \l{declarative/ui-components/tabwidget}{Tab Widget} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Positioners and Layout - \enddiv - \list - \li \l{declarative/positioners}{Row and Column} - \li \l{declarative/righttoleft/layoutmirroring}{Layout Mirroring} - \li \l{declarative/righttoleft/layoutdirection}{Layout Direction} - \li \l{declarative/righttoleft/textalignment}{Text Alignment} - \li \l{declarative/screenorientation}{Screen Orientation} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Data with Models and Views - \enddiv - \list - \li \l{declarative/modelviews/gridview}{GridView} - \li \l{declarative/modelviews/listview}{ListView} - \li \l{declarative/modelviews/pathview}{PathView} - \li \l{declarative/modelviews/package}{Package} - \li \l{declarative/modelviews/visualitemmodel}{VisualItemModel} - \li \l{declarative/modelviews/stringlistmodel}{String ListModel} - \li \l{declarative/modelviews/objectlistmodel}{Object ListModel} - \li \l{declarative/modelviews/abstractitemmodel}{AbstractItemModel} - \li \l{declarative/modelviews/webview}{WebView} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Advance UI Components - \enddiv - \list - \li \l{declarative/modelviews/parallax}{Parallax} - \li \l{declarative/toys/clocks}{Clocks} - \li \l{declarative/toys/corkboards}{Corkboards} - \li \l{declarative/toys/dynamicscene}{Dynamic Scene} - \li \l{declarative/toys/tic-tac-toe}{Tic Tac Toe} - \li \l{declarative/toys/tvtennis}{TV Tennis} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Image Elements - \enddiv - \list - \li \l{declarative/imageelements/borderimage}{BorderImage} - \li \l{declarative/imageelements/image}{Image} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Loading Resources - \enddiv - \list - \li \l{declarative/sqllocalstorage}{SQL Local Storage} - \li \l{declarative/xml/xmlhttprequest}{XmlHttpRequest} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Localization - \enddiv - \list - \li \l{declarative/i18n}{Translation} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Threading - \enddiv - \list - \li \l{declarative/threading/threadedlistmodel}{Threaded ListModel} - \li \l{declarative/threading/workerscript}{WorkerScript Element} - \endlist - \enddiv - \div {class="threecolumn_piece"} - \div {class="heading"} - Graphical Effects - \enddiv - \list - \li \l{declarative/shadereffects}{Shader Effects} - \endlist - \enddiv + \li \l{quick/keyinteraction}{Keyboard Focus} + \li \l{quick/mousearea}{MouseArea} + \li \l{quick/animation}{Animations} + \li \l{quick/positioners}{Positioners} + \li \l{quick/righttoleft}{Right to Left} + \li \l{quick/modelviews}{Model Views} + \li \l{quick/imageelements}{Image Elements} + \li \l{localstorage}{SQL Local Storage} + \li \l{qml/xmlhttprequest}{XmlHttpRequest} + \li \l{qml/i18n}{Translation} + \li \l{quick/threading}{Threading} + \li \l{quick/shadereffects}{Shader Effects} + \endlist \enddiv \div {class="threecolumn_area"} \div {class="heading"} @@ -231,16 +118,16 @@ This set of code samples are part of the collection of \l{Qt Examples}. From Qt C++ to QML \enddiv \list - \li \l {declarative/cppextensions/referenceexamples/adding}{Exporting C++ Classes} - \li \l {declarative/cppextensions/referenceexamples/properties}{Exporting Qt C++ Properties} - \li \l {declarative/cppextensions/referenceexamples/coercion}{C++ Inheritance and Coercion} - \li \l {declarative/cppextensions/referenceexamples/default}{Default Property} - \li \l {declarative/cppextensions/referenceexamples/grouped}{Grouped Properties} - \li \l {declarative/cppextensions/referenceexamples/attached}{Attached Properties} - \li \l {declarative/cppextensions/referenceexamples/signal}{Signal Support} - \li \l {declarative/cppextensions/referenceexamples/methods}{Methods Support} - \li \l {declarative/cppextensions/referenceexamples/valuesource}{Property Value Source} - \li \l {declarative/cppextensions/referenceexamples/binding}{Binding} + \li \l {qml/cppextensions/referenceexamples/adding}{Exporting C++ Classes} + \li \l {qml/cppextensions/referenceexamples/properties}{Exporting Qt C++ Properties} + \li \l {qml/cppextensions/referenceexamples/coercion}{C++ Inheritance and Coercion} + \li \l {qml/cppextensions/referenceexamples/default}{Default Property} + \li \l {qml/cppextensions/referenceexamples/grouped}{Grouped Properties} + \li \l {qml/cppextensions/referenceexamples/attached}{Attached Properties} + \li \l {qml/cppextensions/referenceexamples/signal}{Signal Support} + \li \l {qml/cppextensions/referenceexamples/methods}{Methods Support} + \li \l {qml/cppextensions/referenceexamples/valuesource}{Property Value Source} + \li \l {qml/cppextensions/referenceexamples/binding}{Binding} \endlist \enddiv \div {class="threecolumn_piece"} @@ -248,9 +135,9 @@ This set of code samples are part of the collection of \l{Qt Examples}. Plugins and Resources \enddiv \list - \li \l{declarative/cppextensions/plugins}{Plugins} - \li \l{declarative/cppextensions/imageprovider}{Image Provider} - \li \l{declarative/cppextensions/networkaccessmanagerfactory}{Network Access Manager} + \li \l{qml/cppextensions/plugins}{Plugins} + \li \l{qml/cppextensions/imageprovider}{Image Provider} + \li \l{qml/cppextensions/networkaccessmanagerfactory}{Network Access Manager} \li \l{src/imports/folderlistmodel}{Folder List Model} - a C++ model plugin \endlist \enddiv @@ -259,8 +146,23 @@ This set of code samples are part of the collection of \l{Qt Examples}. Qt UI and QML Integration \enddiv \list - \li \l{declarative-cppextensions-qgraphicslayouts.html}{QGraphicsLayouts} - \li \l{declarative/cppextensions/qwidgets}{QWidgets} + \li \l{qml-cppextensions-qgraphicslayouts.html}{QGraphicsLayouts} + \li \l{qml/cppextensions/qwidgets}{QWidgets} + \endlist + \enddiv + \div {class="threecolumn_piece"} + \div {class="heading"} + UI Components + \enddiv + \list + \li \l{tutorials/ui-components/dialcontrol}{Dial Control} + \li \l{tutorials/ui-components/flipable}{Flipable} + \li \l{tutorials/ui-components/progressbar}{Progress Bar} + \li \l{tutorials/ui-components/scrollbar}{Scroll Bar} + \li \l{tutorials/ui-components/searchbox}{Search Box} + \li \l{tutorials/ui-components/slideswitch}{Slide Switch} + \li \l{tutorials/ui-components/spinner}{Spinner} + \li \l{tutorials/ui-components/tabwidget}{Tab Widget} \endlist \enddiv \enddiv diff --git a/doc/src/images/qml-canvas-example.png b/doc/src/images/qml-canvas-example.png Binary files differnew file mode 100644 index 0000000000..2651ad6ff5 --- /dev/null +++ b/doc/src/images/qml-canvas-example.png diff --git a/doc/src/images/qml-draganddrop-example.png b/doc/src/images/qml-draganddrop-example.png Binary files differnew file mode 100644 index 0000000000..b64f4d63a4 --- /dev/null +++ b/doc/src/images/qml-draganddrop-example.png diff --git a/doc/src/images/qml-keyinteraction-example.png b/doc/src/images/qml-keyinteraction-example.png Binary files differnew file mode 100644 index 0000000000..7f4dd77907 --- /dev/null +++ b/doc/src/images/qml-keyinteraction-example.png diff --git a/doc/src/images/qml-mousearea-example.png b/doc/src/images/qml-mousearea-example.png Binary files differnew file mode 100644 index 0000000000..d93dc92d74 --- /dev/null +++ b/doc/src/images/qml-mousearea-example.png diff --git a/doc/src/images/qml-positioners-example.png b/doc/src/images/qml-positioners-example.png Binary files differindex 90d70ff307..0c02f4a56a 100644 --- a/doc/src/images/qml-positioners-example.png +++ b/doc/src/images/qml-positioners-example.png diff --git a/doc/src/images/qml-righttoleft-example.png b/doc/src/images/qml-righttoleft-example.png Binary files differnew file mode 100644 index 0000000000..03c3c48f10 --- /dev/null +++ b/doc/src/images/qml-righttoleft-example.png diff --git a/doc/src/images/qml-threading-example.png b/doc/src/images/qml-threading-example.png Binary files differnew file mode 100644 index 0000000000..6ed79ab190 --- /dev/null +++ b/doc/src/images/qml-threading-example.png diff --git a/doc/src/qml/qmltypes.qdoc b/doc/src/qml/qmltypes.qdoc index ae4ad47c3d..b75c191616 100644 --- a/doc/src/qml/qmltypes.qdoc +++ b/doc/src/qml/qmltypes.qdoc @@ -70,6 +70,8 @@ int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const c Alternatively, these functions provide a way for other types of C++ types to be visible in the QML context. \list + \li \l qmlRegisterModuleApi() is suited for registering either a QJSValue + or QObject module API (shared instance) into a namespace \li \l qmlRegisterUncreatableType() is suited for attached properties and enum types. \li \l qmlRegisterTypeNotAvailable() is for diff --git a/doc/src/qml/qtdeclarative.qdoc b/doc/src/qml/qtdeclarative.qdoc index 48cc898458..5ad637ab50 100644 --- a/doc/src/qml/qtdeclarative.qdoc +++ b/doc/src/qml/qtdeclarative.qdoc @@ -351,7 +351,7 @@ // third, register the module API provider with QML by calling this function in an initialization function. ... - qmlRegisterModuleApi("Qt.example.qobjectApi", 1, 0, example_qobject_module_api_provider); + qmlRegisterModuleApi<ModuleApiExample>("Qt.example.qobjectApi", 1, 0, example_qobject_module_api_provider); ... \endcode diff --git a/examples/demos/calculator/CalculatorCore/calculator.js b/examples/demos/calculator/CalculatorCore/calculator.js index e2b5692cf3..b4b0d9eea9 100644 --- a/examples/demos/calculator/CalculatorCore/calculator.js +++ b/examples/demos/calculator/CalculatorCore/calculator.js @@ -7,7 +7,7 @@ var timer = 0 function disabled(op) { if (op == "." && display.text.toString().search(/\./) != -1) { return true - } else if (op == squareRoot && display.text.toString().search(/-/) != -1) { + } else if (op == window.squareRoot && display.text.toString().search(/-/) != -1) { return true } else { return false @@ -16,9 +16,9 @@ function disabled(op) { function doOperation(op) { if (op == '*')//Keyboard Aliases - op = multiplication; + op = window.multiplication; if (op == '/') - op = division; + op = window.division; if (disabled(op)) { return } @@ -40,14 +40,14 @@ function doOperation(op) { display.text = Number(display.text.valueOf()) + Number(curVal.valueOf()) } else if (display.currentOperation.text == "-") { display.text = Number(curVal) - Number(display.text.valueOf()) - } else if (display.currentOperation.text == multiplication) { + } else if (display.currentOperation.text == window.multiplication) { display.text = Number(curVal) * Number(display.text.valueOf()) - } else if (display.currentOperation.text == division) { + } else if (display.currentOperation.text == window.division) { display.text = Number(Number(curVal) / Number(display.text.valueOf())).toString() } else if (display.currentOperation.text == "=") { } - if (op == "+" || op == "-" || op == multiplication || op == division) { + if (op == "+" || op == "-" || op == window.multiplication || op == window.division) { display.currentOperation.text = op curVal = display.text.valueOf() return @@ -64,9 +64,9 @@ function doOperation(op) { display.text = (Math.abs(display.text.valueOf())).toString() } else if (op == "Int") { display.text = (Math.floor(display.text.valueOf())).toString() - } else if (op == plusminus) { + } else if (op == window.plusminus) { display.text = (display.text.valueOf() * -1).toString() - } else if (op == squareRoot) { + } else if (op == window.squareRoot) { display.text = (Math.sqrt(display.text.valueOf())).toString() } else if (op == "mc") { memory = 0; @@ -76,7 +76,7 @@ function doOperation(op) { display.text = memory.toString() } else if (op == "m-") { memory = display.text.valueOf() - } else if (op == leftArrow) { + } else if (op == window.leftArrow) { display.text = display.text.toString().slice(0, -1) if (display.text.length == 0) { display.text = "0" diff --git a/examples/demos/calculator/calculator-mobile.qml b/examples/demos/calculator/calculator-mobile.qml index 7ee622f698..d042c756fc 100644 --- a/examples/demos/calculator/calculator-mobile.qml +++ b/examples/demos/calculator/calculator-mobile.qml @@ -95,7 +95,7 @@ Rectangle { Row { spacing: 6 Button { width: column.w; height: column.h; color: 'purple'; operation: "Off" } - Button { width: column.w; height: column.h; color: 'purple'; operation: leftArrow } + Button { width: column.w; height: column.h; color: 'purple'; operation: window.leftArrow } Button { width: column.w; height: column.h; color: 'purple'; operation: "C" } Button { width: column.w; height: column.h; color: 'purple'; operation: "AC" } } @@ -118,12 +118,12 @@ Rectangle { Button { width: grid.w; height: column.h; operation: "7"; color: 'blue' } Button { width: grid.w; height: column.h; operation: "8"; color: 'blue' } Button { width: grid.w; height: column.h; operation: "9"; color: 'blue' } - Button { width: grid.w; height: column.h; operation: division } - Button { width: grid.w; height: column.h; operation: squareRoot } + Button { width: grid.w; height: column.h; operation: window.division } + Button { width: grid.w; height: column.h; operation: window.squareRoot } Button { width: grid.w; height: column.h; operation: "4"; color: 'blue' } Button { width: grid.w; height: column.h; operation: "5"; color: 'blue' } Button { width: grid.w; height: column.h; operation: "6"; color: 'blue' } - Button { width: grid.w; height: column.h; operation: multiplication } + Button { width: grid.w; height: column.h; operation: window.multiplication } Button { width: grid.w; height: column.h; operation: "x^2" } Button { width: grid.w; height: column.h; operation: "1"; color: 'blue' } Button { width: grid.w; height: column.h; operation: "2"; color: 'blue' } @@ -132,7 +132,7 @@ Rectangle { Button { width: grid.w; height: column.h; operation: "1/x" } Button { width: grid.w; height: column.h; operation: "0"; color: 'blue' } Button { width: grid.w; height: column.h; operation: "." } - Button { width: grid.w; height: column.h; operation: plusminus } + Button { width: grid.w; height: column.h; operation: window.plusminus } Button { width: grid.w; height: column.h; operation: "+" } Button { width: grid.w; height: column.h; operation: "="; color: 'red' } } diff --git a/examples/demos/calculator/calculator.qdoc b/examples/demos/calculator/calculator.qdoc index e94bdb4cfd..793948703e 100644 --- a/examples/demos/calculator/calculator.qdoc +++ b/examples/demos/calculator/calculator.qdoc @@ -27,9 +27,9 @@ /*! \title QML Demo - Calculator - \example declarative/calculator + \example demos/calculator \brief This is an example calculator application written in QML. - \image qml-calculator-demo-small.png + \image qml-calculator-example-small.png The Calculator demo implements a simple calculator in QML. It is written for desktop and portrait devices, although on device it supports orientation changes. diff --git a/examples/demos/clocks/clocks.qdoc b/examples/demos/clocks/clocks.qdoc new file mode 100644 index 0000000000..54479bae17 --- /dev/null +++ b/examples/demos/clocks/clocks.qdoc @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms +** and conditions contained in a signed written agreement between you +** and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \title QML Demo - Clocks + \example demos/clocks + \brief This example demonstrates creating components and using them multiple times. + \image qml-clocks-example.png + + This demo give a simple world clock application, containing multiple clocks from around the world. + + In doing this, it reuses a signle Clock component with some slight changes to the parameters. +*/ diff --git a/examples/localstorage/localstorage.qml b/examples/localstorage/localstorage.qml index f967518de8..714774c78e 100644 --- a/examples/localstorage/localstorage.qml +++ b/examples/localstorage/localstorage.qml @@ -43,7 +43,7 @@ import "../shared" as Examples /*! \title QtQuick Examples - Local Storage - \example qtquick/localstorage + \example localstorage \brief This is a collection of QML local storage examples \image qml-localstorage-example.png @@ -63,4 +63,4 @@ Item { addExample("Hello World", "Simple SQL operations with local storage API", Qt.resolvedUrl("hello.qml")); } } -}
\ No newline at end of file +} diff --git a/examples/qml/dynamicscene/content/Button.qml b/examples/qml/dynamicscene/content/Button.qml index 014692274a..ba7db501cc 100644 --- a/examples/qml/dynamicscene/content/Button.qml +++ b/examples/qml/dynamicscene/content/Button.qml @@ -54,16 +54,14 @@ Rectangle { gradient: Gradient { GradientStop { position: 0.0 - color: !mouseArea.pressed ? activePalette.light : activePalette.button + color: !mouseArea.pressed ? "#eeeeee" : "#888888" } GradientStop { position: 1.0 - color: !mouseArea.pressed ? activePalette.button : activePalette.dark + color: !mouseArea.pressed ? "#888888" : "#333333" } } - SystemPalette { id: activePalette } - MouseArea { id: mouseArea anchors.fill: parent @@ -75,6 +73,5 @@ Rectangle { anchors.centerIn:parent font.pointSize: 10 text: parent.text - color: activePalette.buttonText } } diff --git a/examples/qml/dynamicscene/content/Sun.qml b/examples/qml/dynamicscene/content/Sun.qml index b84516eecc..9a956c9855 100644 --- a/examples/qml/dynamicscene/content/Sun.qml +++ b/examples/qml/dynamicscene/content/Sun.qml @@ -47,32 +47,27 @@ Image { property string image: "images/sun.png" source: image - - // once item is created, start moving offscreen - NumberAnimation on y { - to: (window.height / 2) + window.centerOffset - running: created - onRunningChanged: { - if (running) - duration = (window.height + window.centerOffset - sun.y) * 10; - else - state = "OffScreen" - } - } - - states: State { - name: "OffScreen" - StateChangeScript { - script: { sun.created = false; sun.destroy() } - } - } - onCreatedChanged: { if (created) { sun.z = 1; // above the sky but below the ground layer window.activeSuns++; + // once item is created, start moving offscreen + dropYAnim.duration = (window.height + window.centerOffset - sun.y) * 16; + dropAnim.running = true; } else { window.activeSuns--; } } + + SequentialAnimation on y{ + id: dropAnim + running: false + NumberAnimation { + id: dropYAnim + to: (window.height / 2) + window.centerOffset + } + ScriptAction { + script: { sun.created = false; sun.destroy() } + } + } } diff --git a/examples/qml/dynamicscene/dynamicscene.qml b/examples/qml/dynamicscene/dynamicscene.qml index c64df5cfc0..4771742621 100644 --- a/examples/qml/dynamicscene/dynamicscene.qml +++ b/examples/qml/dynamicscene/dynamicscene.qml @@ -38,6 +38,13 @@ ** ****************************************************************************/ +/*! + \title QML Example - Dynamic Scene + \example qml/dynamicscene + \brief This example demonstrates creating components dynamically. + \image qml-dynamicscene-example.png +*/ + import QtQuick 2.0 import QtQuick.Particles 2.0 import "content" @@ -95,7 +102,7 @@ Item { // sky Rectangle { id: sky - anchors { left: parent.left; top: toolbox.bottom; right: parent.right; bottomMargin: -centerOffset; bottom: parent.verticalCenter } + anchors { left: parent.left; top: toolbox.bottom; right: parent.right; bottomMargin: -window.centerOffset; bottom: parent.verticalCenter } gradient: Gradient { GradientStop { id: gradientStopA; position: 0.0; color: "#0E1533" } GradientStop { id: gradientStopB; position: 1.0; color: "#437284" } @@ -127,21 +134,19 @@ Item { Rectangle { id: ground z: 2 // just above the sun so that the sun can set behind it - anchors { left: parent.left; top: parent.verticalCenter; topMargin: centerOffset; right: parent.right; bottom: parent.bottom } + anchors { left: parent.left; top: parent.verticalCenter; topMargin: window.centerOffset; right: parent.right; bottom: parent.bottom } gradient: Gradient { GradientStop { position: 0.0; color: "ForestGreen" } GradientStop { position: 1.0; color: "DarkGreen" } } } - SystemPalette { id: activePalette } - - // right-hand panel + // top panel Rectangle { id: toolbox - height: centerOffset * 2 - color: activePalette.window + height: window.centerOffset * 2 + color: "white" anchors { right: parent.right; top: parent.top; left: parent.left} Column { @@ -192,7 +197,7 @@ Item { } } - Text { text: "Active Suns: " + activeSuns } + Text { text: "Active Suns: " + window.activeSuns } } } @@ -202,7 +207,7 @@ Item { z: 1000 width: parent.width height: popupColumn.height + 16 - color: activePalette.window + color: "white" property bool poppedUp: false property int downY: window.height - (createButton.height + 16) @@ -213,7 +218,6 @@ Item { Column { id: popupColumn y: 8 - anchors.centerIn: parent spacing: 8 Row { @@ -250,7 +254,7 @@ Item { selectByMouse: true wrapMode: TextEdit.WordWrap - text: "import QtQuick 2.0\nImage {\n id: smile\n x: 360 * Math.random()\n y: 180 * Math.random() \n source: 'content/images/face-smile.png'\n NumberAnimation on opacity { \n to: 0; duration: 1500\n }\n Component.onCompleted: smile.destroy(1500);\n}" + text: "import QtQuick 2.0\nImage {\n id: smile\n x: 360 * Math.random()\n y: 40 * Math.random() \n source: 'content/images/face-smile.png'\n NumberAnimation on opacity { \n to: 0; duration: 1500\n }\n Component.onCompleted: smile.destroy(1500);\n}" } } } diff --git a/examples/qml/i18n/i18n.qml b/examples/qml/i18n/i18n.qml index f8bf30d0f0..2ffdd1914f 100644 --- a/examples/qml/i18n/i18n.qml +++ b/examples/qml/i18n/i18n.qml @@ -40,23 +40,29 @@ import QtQuick 2.0 -// -// The QML runtime automatically loads a translation from the i18n subdirectory of the root -// QML file, based on the system language. -// -// The files are created/updated by running: -// -// lupdate i18n.qml -ts i18n/base.ts -// -// Translations for new languages are created by copying i18n/base.ts to i18n/qml_<lang>.ts -// The .ts files can then be edited with Linguist: -// -// linguist i18n/qml_fr.ts -// -// The run-time translation files are then generated by running: -// -// lrelease i18n/*.ts -// +/*! + \title QML Examples - Internationalization + \example qml/i18n + \image qml-i18n-example.png + \brief This is an internationalization example + + The QML runtime automatically loads a translation from the i18n subdirectory of the root + QML file, based on the system language. + + The files are created/updated by running: + + lupdate i18n.qml -ts i18n/base.ts + + Translations for new languages are created by copying i18n/base.ts to i18n/qml_<lang>.ts + The .ts files can then be edited with Linguist: + + linguist i18n/qml_fr.ts + + The run-time translation files are then generated by running: + + lrelease i18n/*.ts +*/ + Rectangle { width: 640; height: 480 diff --git a/examples/quick/accessibility/accessibility.qml b/examples/quick/accessibility/accessibility.qml index e987561bb1..5e4b0f8dcd 100644 --- a/examples/quick/accessibility/accessibility.qml +++ b/examples/quick/accessibility/accessibility.qml @@ -44,7 +44,7 @@ import "content" /*! \title QtQuick Examples - Accessibility - \example qtquick/accessibility + \example quick/accessibility \brief This example has accessible buttons. */ diff --git a/examples/quick/animation/animation.qml b/examples/quick/animation/animation.qml index bca68457d9..f478cb713b 100644 --- a/examples/quick/animation/animation.qml +++ b/examples/quick/animation/animation.qml @@ -43,7 +43,7 @@ import "../../shared" as Examples /*! \title QtQuick Examples - Animation - \example qtquick/animation + \example quick/animation \brief This is a collection of QML Animation examples. \image qml-animations-example.png diff --git a/examples/quick/canvas/canvas.qml b/examples/quick/canvas/canvas.qml index bdb58f4c78..cf3c37a4c3 100644 --- a/examples/quick/canvas/canvas.qml +++ b/examples/quick/canvas/canvas.qml @@ -45,6 +45,7 @@ import "../../shared" as Examples \title QtQuick Examples - Canvas \example quick/canvas \brief This is a collection of QML Canvas examples. + \image qml-canvas-example.png This is a collection of small QML examples relating to Canvas item. Each example is a small QML file emphasizing a particular element or feature. diff --git a/examples/quick/draganddrop/draganddrop.qml b/examples/quick/draganddrop/draganddrop.qml index 88e6d5e021..9f56177f23 100644 --- a/examples/quick/draganddrop/draganddrop.qml +++ b/examples/quick/draganddrop/draganddrop.qml @@ -43,7 +43,7 @@ import "../../shared" as Examples /*! \title QtQuick Examples - Drag and Drop - \example qtquick/draganddrop + \example quick/draganddrop \brief This is a collection of QML drag and drop examples \image qml-draganddrop-example.png diff --git a/examples/quick/imageelements/content/BorderImageSelector.qml b/examples/quick/imageelements/content/BorderImageSelector.qml index f3a534b3cd..8084b512fd 100644 --- a/examples/quick/imageelements/content/BorderImageSelector.qml +++ b/examples/quick/imageelements/content/BorderImageSelector.qml @@ -56,7 +56,7 @@ Item { curIdx += steps; } Image { - source: "../../../shared/images/back.png" + source: "arrow.png" MouseArea{ anchors.fill: parent onClicked: selector.advance(-1) @@ -68,7 +68,7 @@ Item { Behavior on opacity {NumberAnimation{}} } Image { - source: "../../../shared/images/back.png" + source: "arrow.png" mirror: true MouseArea{ anchors.fill: parent diff --git a/examples/quick/imageelements/content/arrow.png b/examples/quick/imageelements/content/arrow.png Binary files differnew file mode 100644 index 0000000000..506ac42fcf --- /dev/null +++ b/examples/quick/imageelements/content/arrow.png diff --git a/examples/quick/imageelements/imageelements.qml b/examples/quick/imageelements/imageelements.qml index bb23ef6979..adfa43a659 100644 --- a/examples/quick/imageelements/imageelements.qml +++ b/examples/quick/imageelements/imageelements.qml @@ -43,7 +43,7 @@ import "../../shared" /*! \title QML Examples - Image Elements - \example declarative/imageelements + \example quick/imageelements \brief This is a collection of QML examples \image qml-imageelements-example.png diff --git a/examples/quick/modelviews/listview/content/SmallText.qml b/examples/quick/modelviews/listview/content/SmallText.qml new file mode 100644 index 0000000000..e27446827f --- /dev/null +++ b/examples/quick/modelviews/listview/content/SmallText.qml @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Text { + font.pixelSize: 12 +} + diff --git a/examples/quick/modelviews/listview/content/TextButton.qml b/examples/quick/modelviews/listview/content/TextButton.qml index 980ee21553..9f0e673f8a 100644 --- a/examples/quick/modelviews/listview/content/TextButton.qml +++ b/examples/quick/modelviews/listview/content/TextButton.qml @@ -52,12 +52,10 @@ Rectangle { radius: 10 gradient: Gradient { - GradientStop { id: gradientStop; position: 0.0; color: palette.light } - GradientStop { position: 1.0; color: palette.button } + GradientStop { id: gradientStop; position: 0.0; color: "#eeeeee" } + GradientStop { position: 1.0; color: "#888888" } } - SystemPalette { id: palette } - MouseArea { id: mouseArea anchors.fill: parent @@ -72,7 +70,7 @@ Rectangle { states: State { name: "pressed" when: mouseArea.pressed - PropertyChanges { target: gradientStop; color: palette.dark } + PropertyChanges { target: gradientStop; color: "#333333" } } } diff --git a/examples/quick/modelviews/listview/expandingdelegates.qml b/examples/quick/modelviews/listview/expandingdelegates.qml index 43a9662422..02d5edc1d1 100644 --- a/examples/quick/modelviews/listview/expandingdelegates.qml +++ b/examples/quick/modelviews/listview/expandingdelegates.qml @@ -106,13 +106,13 @@ Rectangle { font.bold: true; font.pointSize: 16 } - Text { + SmallText { text: "Ingredients" - font.pointSize: 12; font.bold: true + font.bold: true opacity: recipe.detailsOpacity } - Text { + SmallText { text: ingredients wrapMode: Text.WordWrap width: parent.width @@ -127,7 +127,7 @@ Rectangle { anchors { top: topLayout.bottom; topMargin: 10; bottom: parent.bottom; bottomMargin: 10 } opacity: recipe.detailsOpacity - Text { + SmallText { id: methodTitle anchors.top: parent.top text: "Method" diff --git a/examples/quick/modelviews/listview/highlight.qml b/examples/quick/modelviews/listview/highlight.qml index 2dca1f4b18..239a946523 100644 --- a/examples/quick/modelviews/listview/highlight.qml +++ b/examples/quick/modelviews/listview/highlight.qml @@ -56,9 +56,9 @@ Rectangle { id: wrapper width: 200; height: 55 Column { - Text { text: 'Name: ' + name } - Text { text: 'Type: ' + type } - Text { text: 'Age: ' + age } + SmallText { text: 'Name: ' + name } + SmallText { text: 'Type: ' + type } + SmallText { text: 'Age: ' + age } } // indent the item if it is the current item states: State { diff --git a/examples/quick/positioners/positioners-transitions.qml b/examples/quick/positioners/positioners-transitions.qml index 6081c9f8a8..f1b61c1740 100644 --- a/examples/quick/positioners/positioners-transitions.qml +++ b/examples/quick/positioners/positioners-transitions.qml @@ -43,12 +43,12 @@ import QtQuick 2.0 Rectangle { id: page width: 320; height: 480 - property int effectiveOpacity: 1.0 + property real effectiveOpacity: 1.0 Timer { interval: 2000 running: true repeat: true - onTriggered: effectiveOpacity = (effectiveOpacity == 1.0 ? 0.0 : 1.0) + onTriggered: effectiveOpacity = (effectiveOpacity == 1.0 ? 0.0 : 1.0); } Column { diff --git a/examples/quick/righttoleft/righttoleft.qml b/examples/quick/righttoleft/righttoleft.qml index 6561595603..b95f671d95 100644 --- a/examples/quick/righttoleft/righttoleft.qml +++ b/examples/quick/righttoleft/righttoleft.qml @@ -43,7 +43,7 @@ import "../../shared" as Examples /*! \title QtQuick Examples - Right to Left - \example qtquick/Right to Left + \example quick/righttoleft \brief This is a collection of QML Right to Left examples. \image qml-righttoleft-example.png diff --git a/examples/quick/shadereffects/shadereffects.qml b/examples/quick/shadereffects/shadereffects.qml index b76c960a8f..4b3bb2a5e2 100644 --- a/examples/quick/shadereffects/shadereffects.qml +++ b/examples/quick/shadereffects/shadereffects.qml @@ -43,11 +43,12 @@ import QtQuick 2.0 import "content" Rectangle { + id: root width: 320 height: 480 /*! \title QML Examples - Shader Effects - \example declarative/shadereffects + \example quick/shadereffects \image qml-shadereffects-example.png \brief This is a shader effects example @@ -56,10 +57,10 @@ Rectangle { */ property color col: "lightsteelblue" gradient: Gradient { - GradientStop { position: 0.0; color: Qt.tint(col, "#20FFFFFF") } - GradientStop { position: 0.1; color: Qt.tint(col, "#20AAAAAA") } - GradientStop { position: 0.9; color: Qt.tint(col, "#20666666") } - GradientStop { position: 1.0; color: Qt.tint(col, "#20000000") } + GradientStop { position: 0.0; color: Qt.tint(root.col, "#20FFFFFF") } + GradientStop { position: 0.1; color: Qt.tint(root.col, "#20AAAAAA") } + GradientStop { position: 0.9; color: Qt.tint(root.col, "#20666666") } + GradientStop { position: 1.0; color: Qt.tint(root.col, "#20000000") } } ShaderEffectSource { @@ -241,7 +242,7 @@ Rectangle { width: 160 height: 160 property variant source: theSource - property color tint: sliderToColor(colorizeSlider.value) + property color tint: root.sliderToColor(colorizeSlider.value) fragmentShader: " uniform sampler2D source; uniform lowp vec4 tint; diff --git a/examples/quick/text/text.qml b/examples/quick/text/text.qml index e9c40773c9..e8be93cb69 100644 --- a/examples/quick/text/text.qml +++ b/examples/quick/text/text.qml @@ -42,7 +42,7 @@ import QtQuick 2.0 import "../../shared" /*! \title QML Examples - Text - \example declarative/text + \example quick/text \brief This is a collection of QML examples \image qml-text-example.png diff --git a/examples/quick/threading/threading.qml b/examples/quick/threading/threading.qml index 7ba8b8f70d..b96c6975ac 100644 --- a/examples/quick/threading/threading.qml +++ b/examples/quick/threading/threading.qml @@ -43,7 +43,7 @@ import "../../shared" as Examples /*! \title QtQuick Examples - Threading - \example qtquick/threading + \example quick/threading \brief This is a collection of QML Multithreading examples. \image qml-threading-example.png diff --git a/examples/quick/touchinteraction/touchinteraction.qml b/examples/quick/touchinteraction/touchinteraction.qml index 0be625a160..e64c145714 100644 --- a/examples/quick/touchinteraction/touchinteraction.qml +++ b/examples/quick/touchinteraction/touchinteraction.qml @@ -43,7 +43,7 @@ import "../../shared" /*! \title QtQuick Examples - Touch Interaction - \example qtquick/touchinteraction + \example quick/touchinteraction \brief This is a collection of QML Touch Interaction examples. \image qml-touchinteraction-example.png diff --git a/src/3rdparty/javascriptcore/COPYING.LIB b/src/3rdparty/javascriptcore/COPYING.LIB deleted file mode 100644 index 87c4a33dd8..0000000000 --- a/src/3rdparty/javascriptcore/COPYING.LIB +++ /dev/null @@ -1,488 +0,0 @@ - - -NOTE! The LGPL below is copyrighted by the Free Software Foundation, but -the instance of code that it refers to (the kde libraries) are copyrighted -by the authors who actually wrote it. - ---------------------------------------------------------------------------- - GNU LIBRARY GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor - Boston, MA 02110-1301, USA. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the library GPL. It is - numbered 2 because it goes with version 2 of the ordinary GPL.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Library General Public License, applies to some -specially designated Free Software Foundation software, and to any -other libraries whose authors decide to use it. You can use it for -your libraries, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if -you distribute copies of the library, or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link a program with the library, you must provide -complete object files to the recipients so that they can relink them -with the library, after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - Our method of protecting your rights has two steps: (1) copyright -the library, and (2) offer you this license which gives you legal -permission to copy, distribute and/or modify the library. - - Also, for each distributor's protection, we want to make certain -that everyone understands that there is no warranty for this free -library. If the library is modified by someone else and passed on, we -want its recipients to know that what they have is not the original -version, so that any problems introduced by others will not reflect on -the original authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that companies distributing free -software will individually obtain patent licenses, thus in effect -transforming the program into proprietary software. To prevent this, -we have made it clear that any patent must be licensed for everyone's -free use or not licensed at all. - - Most GNU software, including some libraries, is covered by the ordinary -GNU General Public License, which was designed for utility programs. This -license, the GNU Library General Public License, applies to certain -designated libraries. This license is quite different from the ordinary -one; be sure to read it in full, and don't assume that anything in it is -the same as in the ordinary license. - - The reason we have a separate public license for some libraries is that -they blur the distinction we usually make between modifying or adding to a -program and simply using it. Linking a program with a library, without -changing the library, is in some sense simply using the library, and is -analogous to running a utility program or application program. However, in -a textual and legal sense, the linked executable is a combined work, a -derivative of the original library, and the ordinary General Public License -treats it as such. - - Because of this blurred distinction, using the ordinary General -Public License for libraries did not effectively promote software -sharing, because most developers did not use the libraries. We -concluded that weaker conditions might promote sharing better. - - However, unrestricted linking of non-free programs would deprive the -users of those programs of all benefit from the free status of the -libraries themselves. This Library General Public License is intended to -permit developers of non-free programs to use free libraries, while -preserving your freedom as a user of such programs to change the free -libraries that are incorporated in them. (We have not seen how to achieve -this as regards changes in header files, but we have achieved it as regards -changes in the actual functions of the Library.) The hope is that this -will lead to faster development of free libraries. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, while the latter only -works together with the library. - - Note that it is possible for a library to be covered by the ordinary -General Public License rather than by this special one. - - GNU LIBRARY GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library which -contains a notice placed by the copyright holder or other authorized -party saying it may be distributed under the terms of this Library -General Public License (also called "this License"). Each licensee is -addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also compile or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - c) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - d) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the source code distributed need not include anything that is normally -distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Library General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - <one line to give the library's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/src/3rdparty/javascriptcore/DateMath.cpp b/src/3rdparty/javascriptcore/DateMath.cpp deleted file mode 100644 index be99d2ca25..0000000000 --- a/src/3rdparty/javascriptcore/DateMath.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2009 Google Inc. All rights reserved. - * Copyright (C) 2007-2009 Torch Mobile, Inc. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Alternatively, the contents of this file may be used under the terms - * of either the Mozilla Public License Version 1.1, found at - * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public - * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html - * (the "GPL"), in which case the provisions of the MPL or the GPL are - * applicable instead of those above. If you wish to allow use of your - * version of this file only under the terms of one of those two - * licenses (the MPL or the GPL) and not to allow others to use your - * version of this file under the LGPL, indicate your decision by - * deletingthe provisions above and replace them with the notice and - * other provisions required by the MPL or the GPL, as the case may be. - * If you do not delete the provisions above, a recipient may use your - * version of this file under any of the LGPL, the MPL or the GPL. - - * Copyright 2006-2008 the V8 project authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "DateMath.h" - -#include <algorithm> -#include <limits.h> -#include <limits> -#include <time.h> -#include <cmath> - -//#if HAVE(SYS_TIME_H) -#if defined(EXISTS_SYS_TIME) -#include <sys/time.h> -#endif - -//#if HAVE(SYS_TIMEB_H) -#if defined(EXISTS_SYS_TIMEB) -#include <sys/timeb.h> -#endif - -#define NaN std::numeric_limits<double>::quiet_NaN() - -using namespace QV8DateConverter::WTF; - -namespace QV8DateConverter { - -namespace WTF { - -/* Constants */ - -static const double minutesPerDay = 24.0 * 60.0; -static const double secondsPerDay = 24.0 * 60.0 * 60.0; -static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0; - -static const double usecPerSec = 1000000.0; - -static const double maxUnixTime = 2145859200.0; // 12/31/2037 -// ECMAScript asks not to support for a date of which total -// millisecond value is larger than the following value. -// See 15.9.1.14 of ECMA-262 5th edition. -static const double maxECMAScriptTime = 8.64E15; - -// Day of year for the first day of each month, where index 0 is January, and day 0 is January 1. -// First for non-leap years, then for leap years. -static const int firstDayOfMonth[2][12] = { - {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, - {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} -}; - -static inline bool isLeapYear(int year) -{ - if (year % 4 != 0) - return false; - if (year % 400 == 0) - return true; - if (year % 100 == 0) - return false; - return true; -} - -static inline int daysInYear(int year) -{ - return 365 + isLeapYear(year); -} - -static inline double daysFrom1970ToYear(int year) -{ - // The Gregorian Calendar rules for leap years: - // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years. - // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years. - // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years. - - static const int leapDaysBefore1971By4Rule = 1970 / 4; - static const int excludedLeapDaysBefore1971By100Rule = 1970 / 100; - static const int leapDaysBefore1971By400Rule = 1970 / 400; - - const double yearMinusOne = year - 1; - const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule; - const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule; - const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule; - - return 365.0 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; -} - -static inline double msToDays(double ms) -{ - return floor(ms / msPerDay); -} - -int msToYear(double ms) -{ - int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970); - double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); - if (msFromApproxYearTo1970 > ms) - return approxYear - 1; - if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms) - return approxYear + 1; - return approxYear; -} - -int dayInYear(double ms, int year) -{ - return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year)); -} - -static inline double msToMilliseconds(double ms) -{ - double result = fmod(ms, msPerDay); - if (result < 0) - result += msPerDay; - return result; -} - -// 0: Sunday, 1: Monday, etc. -static inline int msToWeekDay(double ms) -{ - int wd = (static_cast<int>(msToDays(ms)) + 4) % 7; - if (wd < 0) - wd += 7; - return wd; -} - -static inline int msToSeconds(double ms) -{ - double result = fmod(floor(ms / msPerSecond), secondsPerMinute); - if (result < 0) - result += secondsPerMinute; - return static_cast<int>(result); -} - -static inline int msToMinutes(double ms) -{ - double result = fmod(floor(ms / msPerMinute), minutesPerHour); - if (result < 0) - result += minutesPerHour; - return static_cast<int>(result); -} - -static inline int msToHours(double ms) -{ - double result = fmod(floor(ms/msPerHour), hoursPerDay); - if (result < 0) - result += hoursPerDay; - return static_cast<int>(result); -} - -int monthFromDayInYear(int dayInYear, bool leapYear) -{ - const int d = dayInYear; - int step; - - if (d < (step = 31)) - return 0; - step += (leapYear ? 29 : 28); - if (d < step) - return 1; - if (d < (step += 31)) - return 2; - if (d < (step += 30)) - return 3; - if (d < (step += 31)) - return 4; - if (d < (step += 30)) - return 5; - if (d < (step += 31)) - return 6; - if (d < (step += 31)) - return 7; - if (d < (step += 30)) - return 8; - if (d < (step += 31)) - return 9; - if (d < (step += 30)) - return 10; - return 11; -} - -static inline bool checkMonth(int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) -{ - startDayOfThisMonth = startDayOfNextMonth; - startDayOfNextMonth += daysInThisMonth; - return (dayInYear <= startDayOfNextMonth); -} - -int dayInMonthFromDayInYear(int dayInYear, bool leapYear) -{ - const int d = dayInYear; - int step; - int next = 30; - - if (d <= next) - return d + 1; - const int daysInFeb = (leapYear ? 29 : 28); - if (checkMonth(d, step, next, daysInFeb)) - return d - step; - if (checkMonth(d, step, next, 31)) - return d - step; - if (checkMonth(d, step, next, 30)) - return d - step; - if (checkMonth(d, step, next, 31)) - return d - step; - if (checkMonth(d, step, next, 30)) - return d - step; - if (checkMonth(d, step, next, 31)) - return d - step; - if (checkMonth(d, step, next, 31)) - return d - step; - if (checkMonth(d, step, next, 30)) - return d - step; - if (checkMonth(d, step, next, 31)) - return d - step; - if (checkMonth(d, step, next, 30)) - return d - step; - step = next; - return d - step; -} - -static inline int monthToDayInYear(int month, bool isLeapYear) -{ - return firstDayOfMonth[isLeapYear][month]; -} - -static inline double timeToMS(double hour, double min, double sec, double ms) -{ - return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms); -} - -double dateToDaysFrom1970(int year, int month, int day) -{ - year += month / 12; - - month %= 12; - if (month < 0) { - month += 12; - --year; - } - - double yearday = floor(daysFrom1970ToYear(year)); - int monthday = monthToDayInYear(month, isLeapYear(year)); - - return yearday + monthday + day - 1; -} - -static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second) -{ - double days = (day - 32075) - + floor(1461 * (year + 4800.0 + (mon - 14) / 12) / 4) - + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 - - floor(3 * ((year + 4900.0 + (mon - 14) / 12) / 100) / 4) - - 2440588; - return ((days * hoursPerDay + hour) * minutesPerHour + minute) * secondsPerMinute + second; -} - -// We follow the recommendation of RFC 2822 to consider all -// obsolete time zones not listed here equivalent to "-0000". -static const struct KnownZone { - const - char tzName[4]; - int tzOffset; -} known_zones[] = { - { "UT", 0 }, - { "GMT", 0 }, - { "EST", -300 }, - { "EDT", -240 }, - { "CST", -360 }, - { "CDT", -300 }, - { "MST", -420 }, - { "MDT", -360 }, - { "PST", -480 }, - { "PDT", -420 } -}; - -double timeClip(double t) -{ -#if defined(_MSC_VER) - if (!_finite(t) || fabs(t) > maxECMAScriptTime) - return NaN; - return t >= 0 ? floor(t) : ceil(t); -#else - -#if defined(__QNXNTO__) - if (!isfinite(t) || fabs(t) > maxECMAScriptTime) - return NaN; -#else - if (!std::isfinite(t) || fabs(t) > maxECMAScriptTime) - return NaN; -#endif - - return trunc(t); -#endif -} -} // namespace WTF - -namespace JSC { - -double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds) -{ - double day = dateToDaysFrom1970(t.year + 1900, t.month, t.monthDay); - double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); - double result = (day * WTF::msPerDay) + ms; - - return result; -} - -// input is UTC -void msToGregorianDateTime(double ms, GregorianDateTime& tm) -{ - const int year = msToYear(ms); - tm.second = msToSeconds(ms); - tm.minute = msToMinutes(ms); - tm.hour = msToHours(ms); - tm.weekDay = msToWeekDay(ms); - tm.yearDay = dayInYear(ms, year); - tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); - tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); - tm.year = year - 1900; - tm.isDST = false; - tm.utcOffset = static_cast<long>(0); // no ExecState :. cannot calculate offset. Assume UTC output. - tm.timeZone = NULL; -} - -} // namespace JSC - -} // namespace QV8DateConverter - diff --git a/src/3rdparty/javascriptcore/DateMath.h b/src/3rdparty/javascriptcore/DateMath.h deleted file mode 100644 index 5adb0d358c..0000000000 --- a/src/3rdparty/javascriptcore/DateMath.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2009 Google Inc. All rights reserved. - * - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (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.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - */ - -#ifndef DateMath_h -#define DateMath_h - -#include <math.h> -#include <string.h> -#include <time.h> - -namespace QV8DateConverter { - -namespace WTF { - -double timeClip(double); - -const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; -const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - -const double hoursPerDay = 24.0; -const double minutesPerHour = 60.0; -const double secondsPerHour = 60.0 * 60.0; -const double secondsPerMinute = 60.0; -const double msPerSecond = 1000.0; -const double msPerMinute = 60.0 * 1000.0; -const double msPerHour = 60.0 * 60.0 * 1000.0; -const double msPerDay = 24.0 * 60.0 * 60.0 * 1000.0; -const double msPerMonth = 2592000000.0; - -// Returns the number of days from 1970-01-01 to the specified date. -double dateToDaysFrom1970(int year, int month, int day); -int msToYear(double ms); -int dayInYear(double ms, int year); -int monthFromDayInYear(int dayInYear, bool leapYear); -int dayInMonthFromDayInYear(int dayInYear, bool leapYear); - -} // namespace WTF - -using WTF::dateToDaysFrom1970; -using WTF::dayInMonthFromDayInYear; -using WTF::dayInYear; -using WTF::minutesPerHour; -using WTF::monthFromDayInYear; -using WTF::msPerDay; -using WTF::msPerSecond; -using WTF::msToYear; -using WTF::secondsPerMinute; - -namespace JSC { -struct GregorianDateTime; - -void msToGregorianDateTime(double, GregorianDateTime&); -double gregorianDateTimeToMS(const GregorianDateTime&, double); - -// Intentionally overridding the default tm of the system. -// The members of tm differ on various operating systems. -struct GregorianDateTime { - GregorianDateTime() - : second(0) - , minute(0) - , hour(0) - , weekDay(0) - , monthDay(0) - , yearDay(0) - , month(0) - , year(0) - , isDST(0) - , utcOffset(0) - , timeZone(0) - { - } - - ~GregorianDateTime() - { - delete [] timeZone; - } - - GregorianDateTime(const tm& inTm) - : second(inTm.tm_sec) - , minute(inTm.tm_min) - , hour(inTm.tm_hour) - , weekDay(inTm.tm_wday) - , monthDay(inTm.tm_mday) - , yearDay(inTm.tm_yday) - , month(inTm.tm_mon) - , year(inTm.tm_year) - , isDST(inTm.tm_isdst) - { - utcOffset = static_cast<int>(0); - timeZone = 0; - } - - operator tm() const - { - tm ret; - memset(&ret, 0, sizeof(ret)); - - ret.tm_sec = second; - ret.tm_min = minute; - ret.tm_hour = hour; - ret.tm_wday = weekDay; - ret.tm_mday = monthDay; - ret.tm_yday = yearDay; - ret.tm_mon = month; - ret.tm_year = year; - ret.tm_isdst = isDST; - return ret; - } - - void copyFrom(const GregorianDateTime& rhs) - { - second = rhs.second; - minute = rhs.minute; - hour = rhs.hour; - weekDay = rhs.weekDay; - monthDay = rhs.monthDay; - yearDay = rhs.yearDay; - month = rhs.month; - year = rhs.year; - isDST = rhs.isDST; - utcOffset = rhs.utcOffset; - if (rhs.timeZone) { - int inZoneSize = strlen(rhs.timeZone) + 1; - timeZone = new char[inZoneSize]; - strncpy(timeZone, rhs.timeZone, inZoneSize); - } else - timeZone = 0; - } - - int second; - int minute; - int hour; - int weekDay; - int monthDay; - int yearDay; - int month; - int year; - int isDST; - int utcOffset; - char* timeZone; -}; - -static inline int gmtoffset(const GregorianDateTime& t) -{ - return t.utcOffset; -} - -} // namespace JSC - -} // namespace QV8DateConverter - -#endif // DateMath_h diff --git a/src/3rdparty/javascriptcore/VERSION b/src/3rdparty/javascriptcore/VERSION deleted file mode 100644 index af6229afcb..0000000000 --- a/src/3rdparty/javascriptcore/VERSION +++ /dev/null @@ -1,15 +0,0 @@ -This is a snapshot of the files: DateMath.h and DateMath.cpp -from a snapshot of JavaScriptCore from - - git://gitorious.org/qtwebkit/qtwebkit.git - -The commit imported was from the - - javascriptcore-snapshot-27012011 branch/tag - -and has the sha1 checksum - - 3ab0f621048fbeb480b687a28ed31d92d8506150 - -The two files were then modified slightly so that unneeded -functionality was removed (mostly regarding timezone offset). diff --git a/src/imports/imports.pro b/src/imports/imports.pro index b62275b009..f7861ac009 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -1,5 +1,5 @@ TEMPLATE = subdirs -SUBDIRS += qtquick2 folderlistmodel localstorage +SUBDIRS += qtquick2 particles window folderlistmodel localstorage contains(QT_CONFIG, qmltest): SUBDIRS += testlib contains(QT_CONFIG, xmlpatterns) : SUBDIRS += xmllistmodel diff --git a/src/imports/particles/particles.pro b/src/imports/particles/particles.pro new file mode 100644 index 0000000000..dc3198d124 --- /dev/null +++ b/src/imports/particles/particles.pro @@ -0,0 +1,19 @@ +TARGET = particlesplugin +TARGETPATH = QtQuick/Particles.2 +include(../qimportbase.pri) + +SOURCES += \ + plugin.cpp + +QT += quick-private qml-private + +OTHER_FILES += \ + qmldir + +DESTDIR = $$QT.qml.imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +INSTALLS += target qmldir diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/selectiontool.h b/src/imports/particles/plugin.cpp index c2aa26a1a7..b5680b5ed1 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/selectiontool.h +++ b/src/imports/particles/plugin.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage @@ -39,48 +39,27 @@ ** ****************************************************************************/ -#ifndef SELECTIONTOOL_H -#define SELECTIONTOOL_H +#include <QtQml/qqmlextensionplugin.h> -#include "abstracttool.h" +#include <private/qquickparticlesmodule_p.h> -#include <QtCore/QList> -#include <QtCore/QPoint> +QT_BEGIN_NAMESPACE -QT_FORWARD_DECLARE_CLASS(QQuickItem) - -namespace QmlJSDebugger { -namespace QtQuick2 { - -class QQuickViewInspector; -class HoverHighlight; - -class SelectionTool : public AbstractTool +//![class decl] +class QtQuick2ParticlesPlugin : public QQmlExtensionPlugin { Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") public: - explicit SelectionTool(QQuickViewInspector *inspector); - - void leaveEvent(QEvent *); - - void mousePressEvent(QMouseEvent *); - void mouseMoveEvent(QMouseEvent *) {} - void mouseReleaseEvent(QMouseEvent *) {} - void mouseDoubleClickEvent(QMouseEvent *) {} - - void hoverMoveEvent(QMouseEvent *); - void wheelEvent(QWheelEvent *) {} - - void keyPressEvent(QKeyEvent *) {} - void keyReleaseEvent(QKeyEvent *) {} - -private: - QQuickViewInspector *inspector() const; - - HoverHighlight *m_hoverHighlight; + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Particles")); + Q_UNUSED(uri); + QQuickParticlesModule::defineModule(); + } }; +//![class decl] -} // namespace QtQuick2 -} // namespace QmlJSDebugger +QT_END_NAMESPACE -#endif // SELECTIONTOOL_H +#include "plugin.moc" diff --git a/src/imports/particles/qmldir b/src/imports/particles/qmldir new file mode 100644 index 0000000000..593915f83f --- /dev/null +++ b/src/imports/particles/qmldir @@ -0,0 +1 @@ +plugin particlesplugin diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 1f9de5e998..371bba9fde 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -607,6 +607,10 @@ Item { functionsToRun.splice(index, 1) } qtest_results.functionName = prop + + if (!(datafunc in testCase)) + datafunc = "init_data"; + if (datafunc in testCase) { if (qtest_runInternal(datafunc)) { var table = qtest_testCaseResult @@ -624,9 +628,13 @@ Item { qtest_runFunction(prop, row) qtest_results.dataTag = "" } - if (!haveData) - qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()" - , util.callerFile(), util.callerLine()); + if (!haveData) { + if (datafunc === "init_data") + qtest_runFunction(prop, null, isBenchmark) + else + qtest_results.warn("no data supplied for " + prop + "() by " + datafunc + "()" + , util.callerFile(), util.callerLine()); + } qtest_results.clearTestTable() } } else if (isBenchmark) { diff --git a/src/imports/testlib/testcase.qdoc b/src/imports/testlib/testcase.qdoc index f928cdaa44..29f7a2d287 100644 --- a/src/imports/testlib/testcase.qdoc +++ b/src/imports/testlib/testcase.qdoc @@ -51,8 +51,8 @@ element: \code - import QtQuick 1.0 - import QtQuickTest 1.0 + import QtQuick 2.0 + import QtTest 1.0 TestCase { name: "MathTests" @@ -97,15 +97,24 @@ \section1 Data-driven tests Table data can be provided to a test using a function name that ends - with "_data": + with "_data". Alternatively, the \c init_data() function can be used + to provide default test data for all test functions in a TestCase element: + \code - import QtQuick 1.0 - import QtQuickTest 1.0 + import QtQuick 2.0 + import QtTest 1.0 TestCase { name: "DataTests" + function init_data() { + return [ + {tag:"init_data_1", a:1, b:2, answer: 3}, + {tag:"init_data_2", a:2, b:4, answer: 6} + ]; + } + function test_table_data() { return [ {tag: "2 + 2 = 4", a: 2, b: 2, answer: 4 }, @@ -114,6 +123,12 @@ } function test_table(data) { + //data comes from test_table_data + compare(data.a + data.b, data.answer) + } + + function test__default_table(data) { + //data comes from init_data compare(data.a + data.b, data.answer) } } diff --git a/src/imports/window/plugin.cpp b/src/imports/window/plugin.cpp new file mode 100644 index 0000000000..b0c8c90890 --- /dev/null +++ b/src/imports/window/plugin.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqmlextensionplugin.h> + +#include <private/qquickwindowmodule_p.h> + +QT_BEGIN_NAMESPACE + +//![class decl] +class QtQuick2WindowPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Window")); + Q_UNUSED(uri); + QQuickWindowModule::defineModule(); + } +}; +//![class decl] + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/window/qmldir b/src/imports/window/qmldir new file mode 100644 index 0000000000..32844a6ed6 --- /dev/null +++ b/src/imports/window/qmldir @@ -0,0 +1 @@ +plugin windowplugin diff --git a/src/imports/window/window.pro b/src/imports/window/window.pro new file mode 100644 index 0000000000..42b6f5911d --- /dev/null +++ b/src/imports/window/window.pro @@ -0,0 +1,19 @@ +TARGET = windowplugin +TARGETPATH = QtQuick/Window.2 +include(../qimportbase.pri) + +SOURCES += \ + plugin.cpp + +QT += quick-private qml-private + +OTHER_FILES += \ + qmldir + +DESTDIR = $$QT.qml.imports/$$TARGETPATH +target.path = $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +qmldir.files += $$PWD/qmldir +qmldir.path += $$[QT_INSTALL_IMPORTS]/$$TARGETPATH + +INSTALLS += target qmldir diff --git a/src/plugins/accessible/quick/main.cpp b/src/plugins/accessible/quick/main.cpp index 2c75e594c2..6592c59e0f 100644 --- a/src/plugins/accessible/quick/main.cpp +++ b/src/plugins/accessible/quick/main.cpp @@ -46,6 +46,7 @@ #include <QtQuick/QQuickView> #include <QtQuick/QQuickItem> +#include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickaccessibleattached_p.h> #include <qaccessibleplugin.h> @@ -86,8 +87,11 @@ QAccessibleInterface *AccessibleQuickFactory::create(const QString &classname, Q if (classname == QLatin1String("QQuickView")) { return new QAccessibleQuickView(qobject_cast<QQuickView *>(object)); // FIXME } else if (classname == QLatin1String("QQuickItem")) { - QQuickItem * item = qobject_cast<QQuickItem *>(object); + QQuickItem *item = qobject_cast<QQuickItem *>(object); Q_ASSERT(item); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + if (!itemPrivate->isAccessible) + return 0; QVariant v = QQuickAccessibleAttached::property(item, "role"); bool ok; diff --git a/src/plugins/accessible/quick/qaccessiblequickview.cpp b/src/plugins/accessible/quick/qaccessiblequickview.cpp index 2df1f243b8..ed8167f4d1 100644 --- a/src/plugins/accessible/quick/qaccessiblequickview.cpp +++ b/src/plugins/accessible/quick/qaccessiblequickview.cpp @@ -127,7 +127,11 @@ static QQuickItem *childAt_helper(QQuickItem *item, int x, int y) } QScopedPointer<QAccessibleInterface> accessibleInterface(QAccessible::queryAccessibleInterface(item)); - if (accessibleInterface && accessibleInterface->childCount() == 0) { + // this item has no Accessible attached property + if (!accessibleInterface) + return 0; + + if (accessibleInterface->childCount() == 0) { return (itemScreenRect(item).contains(x, y)) ? item : 0; } @@ -155,6 +159,7 @@ QAccessibleInterface *QAccessibleQuickView::childAt(int x, int y) const if (root) { if (QQuickItem *item = childAt_helper(root, x, y)) return QAccessible::queryAccessibleInterface(item); + return QAccessible::queryAccessibleInterface(root); } return 0; } diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index ae42ba1ba4..9ef8c7ab72 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,3 +1,5 @@ TEMPLATE = subdirs SUBDIRS += qmltooling -SUBDIRS += accessible +contains(QT_CONFIG, accessibility) { + SUBDIRS += accessible +} diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/highlight.cpp b/src/plugins/qmltooling/qmldbg_qtquick2/highlight.cpp index bb4048ee92..da959933f7 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/highlight.cpp +++ b/src/plugins/qmltooling/qmldbg_qtquick2/highlight.cpp @@ -81,14 +81,14 @@ void Highlight::adjust() } -void SelectionHighlight::paint(QPainter *painter) +void HoverHighlight::paint(QPainter *painter) { painter->setPen(QColor(108, 141, 221)); painter->drawRect(QRect(0, 0, width() - 1, height() - 1)); } -void HoverHighlight::paint(QPainter *painter) +void SelectionHighlight::paint(QPainter *painter) { painter->setPen(QPen(QColor(0, 22, 159))); painter->drawRect(QRect(1, 1, width() - 3, height() - 3)); diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/zoomtool.cpp b/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.cpp index 171f1a52e9..67b12822be 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/zoomtool.cpp +++ b/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.cpp @@ -39,14 +39,19 @@ ** ****************************************************************************/ -#include "zoomtool.h" +#include "inspecttool.h" + +#include "highlight.h" #include "qquickviewinspector.h" #include <QtCore/QLineF> + #include <QtGui/QMouseEvent> #include <QtGui/QWheelEvent> #include <QtGui/QTouchEvent> #include <QtGui/QKeyEvent> +#include <QtGui/QGuiApplication> +#include <QtGui/QStyleHints> #include <QtQuick/QQuickView> #include <QtQuick/QQuickItem> @@ -54,67 +59,107 @@ namespace QmlJSDebugger { namespace QtQuick2 { -ZoomTool::ZoomTool(QQuickViewInspector *inspector, QQuickView *view) : +InspectTool::InspectTool(QQuickViewInspector *inspector, QQuickView *view) : AbstractTool(inspector), + m_originalSmooth(view->rootItem()->smooth()), m_dragStarted(false), m_pinchStarted(false), + m_didPressAndHold(false), + m_tapEvent(false), + m_rootItem(view->rootItem()), + m_originalPosition(view->rootItem()->pos()), m_currentScale(1.0f), - m_smoothScaleFactor(0.05f), + m_smoothScaleFactor(Constants::ZoomSnapDelta), m_minScale(0.125f), m_maxScale(48.0f), - m_tapScaleCounter(0) + m_originalScale(view->rootItem()->scale()), + m_touchTimestamp(0), + m_hoverHighlight(new HoverHighlight(inspector->overlay())), + m_lastItem(0), + m_lastClickedItem(0) { - m_rootItem = view->rootItem(); - m_originalSmooth = m_rootItem->smooth(); - if (!m_originalSmooth) - m_rootItem->setSmooth(true); - m_originalPosition = m_rootItem->pos(); - m_originalScale = m_rootItem->scale(); + //Press and Hold Timer + m_pressAndHoldTimer.setSingleShot(true); + m_pressAndHoldTimer.setInterval(Constants::PressAndHoldTimeout); + connect(&m_pressAndHoldTimer, SIGNAL(timeout()), SLOT(zoomTo100())); + enable(true); } -ZoomTool::~ZoomTool() +InspectTool::~InspectTool() { - // restoring the original states. - m_rootItem->setScale(m_originalScale); - m_rootItem->setPos(m_originalPosition); - if (!m_originalSmooth) - m_rootItem->setSmooth(m_originalSmooth); + enable(false); } -void ZoomTool::mousePressEvent(QMouseEvent *event) +void InspectTool::enable(bool enable) { - m_mousePosition = event->posF(); - if (event->buttons() & Qt::LeftButton) { - m_dragStartPosition = event->posF(); - m_dragStarted = false; + if (!enable) { + inspector()->setSelectedItems(QList<QQuickItem*>()); + // restoring the original states. + if (m_rootItem) { + m_rootItem->setScale(m_originalScale); + m_rootItem->setPos(m_originalPosition); + m_rootItem->setSmooth(m_originalSmooth); + } + } else { + if (m_rootItem) { + m_originalSmooth = m_rootItem->smooth(); + m_originalScale = m_rootItem->scale(); + m_originalPosition = m_rootItem->pos(); + m_rootItem->setSmooth(true); + } } } -void ZoomTool::mouseMoveEvent(QMouseEvent *event) +void InspectTool::leaveEvent(QEvent *) { - if (m_pinchStarted) - return; + m_hoverHighlight->setVisible(false); +} +void InspectTool::mousePressEvent(QMouseEvent *event) +{ m_mousePosition = event->posF(); - if (!m_dragStarted - && event->buttons() & Qt::LeftButton - && ((m_dragStartPosition - event->posF()).manhattanLength() - > Constants::DragStartDistance)) { - m_dragStarted = true; - } - if (m_dragStarted) { - m_adjustedOrigin += event->posF() - m_dragStartPosition; - m_dragStartPosition = event->posF(); - m_rootItem->setPos(m_adjustedOrigin); + if (event->button() == Qt::LeftButton) { + m_pressAndHoldTimer.start(); + initializeDrag(event->posF()); } } -void ZoomTool::hoverMoveEvent(QMouseEvent *event) +void InspectTool::mouseReleaseEvent(QMouseEvent *event) +{ + m_mousePosition = event->posF(); + m_pressAndHoldTimer.stop(); + if (event->button() == Qt::LeftButton) + selectItem(); +} + +void InspectTool::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_mousePosition = event->posF(); + m_pressAndHoldTimer.stop(); + if (event->button() == Qt::LeftButton) + selectNextItem(); +} + +void InspectTool::mouseMoveEvent(QMouseEvent *event) +{ + m_mousePosition = event->posF(); + moveItem(event->buttons() & Qt::LeftButton); +} + +void InspectTool::hoverMoveEvent(QMouseEvent *event) { m_mousePosition = event->posF(); + m_pressAndHoldTimer.stop(); + QQuickItem *item = inspector()->topVisibleItemAt(event->pos()); + if (!item || item == m_lastClickedItem) { + m_hoverHighlight->setVisible(false); + } else { + m_hoverHighlight->setItem(item); + m_hoverHighlight->setVisible(true); + } } -void ZoomTool::wheelEvent(QWheelEvent *event) +void InspectTool::wheelEvent(QWheelEvent *event) { if (event->orientation() != Qt::Vertical) return; @@ -133,13 +178,7 @@ void ZoomTool::wheelEvent(QWheelEvent *event) } } -void ZoomTool::mouseDoubleClickEvent(QMouseEvent *event) -{ - m_mousePosition = event->posF(); - zoomTo100(); -} - -void ZoomTool::keyReleaseEvent(QKeyEvent *event) +void InspectTool::keyReleaseEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_Plus: @@ -166,16 +205,31 @@ void ZoomTool::keyReleaseEvent(QKeyEvent *event) } } -void ZoomTool::touchEvent(QTouchEvent *event) +void InspectTool::touchEvent(QTouchEvent *event) { QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints(); switch (event->type()) { case QEvent::TouchBegin: - // fall through.. + if (touchPoints.count() == 1 && (event->touchPointStates() & Qt::TouchPointPressed)) { + if (!m_pressAndHoldTimer.isActive()) + m_pressAndHoldTimer.start(); + m_mousePosition = touchPoints.first().pos(); + initializeDrag(touchPoints.first().pos()); + m_tapEvent = true; + } else { + m_tapEvent = false; + } + break; case QEvent::TouchUpdate: { - if ((touchPoints.count() == 2) - && (!(event->touchPointStates() & Qt::TouchPointReleased))) { + if (touchPoints.count() > 1) + m_tapEvent = false; + if ((touchPoints.count() == 1) + && (event->touchPointStates() & Qt::TouchPointMoved)) { + m_mousePosition = touchPoints.first().pos(); + moveItem(true); + } else if ((touchPoints.count() == 2) + && (!(event->touchPointStates() & Qt::TouchPointReleased))) { // determine scale factor const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first(); const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last(); @@ -188,21 +242,27 @@ void ZoomTool::touchEvent(QTouchEvent *event) QPointF newcenter = (touchPoint0.pos() + touchPoint1.pos()) / 2; m_pinchStarted = true; - m_tapScaleCounter = 0; scaleView(touchScaleFactor, newcenter, oldcenter); } break; } case QEvent::TouchEnd: { + m_pressAndHoldTimer.stop(); if (m_pinchStarted) { m_pinchStarted = false; - } else if ((touchPoints.count() == 1) - &&(!m_dragStarted)) { - ++m_tapScaleCounter; - qreal factor = 1.0f + (1.0f / (m_tapScaleCounter + 1)); - scaleView(factor, touchPoints.first().pos(), - touchPoints.first().pos()); } + if (touchPoints.count() == 1 && !m_dragStarted && + !m_didPressAndHold && m_tapEvent) { + m_tapEvent = false; + bool doubleTap = event->timestamp() - m_touchTimestamp + < static_cast<ulong>(qApp->styleHints()->mouseDoubleClickInterval()); + if (doubleTap) + selectNextItem(); + else + selectItem(); + m_touchTimestamp = event->timestamp(); + } + m_didPressAndHold = false; break; } default: @@ -210,8 +270,9 @@ void ZoomTool::touchEvent(QTouchEvent *event) } } -void ZoomTool::scaleView(const qreal &factor, const QPointF &newcenter, const QPointF &oldcenter) +void InspectTool::scaleView(const qreal &factor, const QPointF &newcenter, const QPointF &oldcenter) { + m_pressAndHoldTimer.stop(); if (((m_currentScale * factor) > m_maxScale) || ((m_currentScale * factor) < m_minScale)) { return; @@ -224,29 +285,29 @@ void ZoomTool::scaleView(const qreal &factor, const QPointF &newcenter, const QP m_rootItem->setPos(m_adjustedOrigin); } -void ZoomTool::zoomIn() +void InspectTool::zoomIn() { qreal newScale = nextZoomScale(ZoomIn); scaleView(newScale / m_currentScale, m_mousePosition, m_mousePosition); } -void ZoomTool::zoomOut() +void InspectTool::zoomOut() { qreal newScale = nextZoomScale(ZoomOut); scaleView(newScale / m_currentScale, m_mousePosition, m_mousePosition); } -void ZoomTool::zoomTo100() +void InspectTool::zoomTo100() { + m_didPressAndHold = true; m_currentScale = 1.0; m_adjustedOrigin = QPointF(0, 0); - m_tapScaleCounter = 0; m_rootItem->setPos(m_adjustedOrigin); m_rootItem->setScale(m_currentScale); } -qreal ZoomTool::nextZoomScale(ZoomDirection direction) +qreal InspectTool::nextZoomScale(ZoomDirection direction) { static QList<qreal> zoomScales = QList<qreal>() @@ -286,5 +347,67 @@ qreal ZoomTool::nextZoomScale(ZoomDirection direction) return 1.0f; } +void InspectTool::initializeDrag(const QPointF &pos) +{ + m_dragStartPosition = pos; + m_dragStarted = false; +} + +void InspectTool::dragItemToPosition() +{ + m_adjustedOrigin += m_mousePosition - m_dragStartPosition; + m_dragStartPosition = m_mousePosition; + m_rootItem->setPos(m_adjustedOrigin); +} + +void InspectTool::moveItem(bool valid) +{ + if (m_pinchStarted) + return; + + if (!m_dragStarted + && valid + && ((m_dragStartPosition - m_mousePosition).manhattanLength() + > qApp->styleHints()->startDragDistance())) { + m_pressAndHoldTimer.stop(); + m_dragStarted = true; + } + if (m_dragStarted) + dragItemToPosition(); +} + +void InspectTool::selectNextItem() +{ + if (m_lastClickedItem != inspector()->topVisibleItemAt(m_mousePosition)) + return; + QList<QQuickItem*> items = inspector()->itemsAt(m_mousePosition); + for (int i = 0; i < items.count(); i++) { + if (m_lastItem == items[i]) { + if (i + 1 < items.count()) + m_lastItem = items[i+1]; + else + m_lastItem = items[0]; + inspector()->setSelectedItems(QList<QQuickItem*>() << m_lastItem); + break; + } + } +} + +void InspectTool::selectItem() +{ + if (!inspector()->topVisibleItemAt(m_mousePosition)) + return; + if (m_lastClickedItem == inspector()->topVisibleItemAt(m_mousePosition)) + return; + m_lastClickedItem = inspector()->topVisibleItemAt(m_mousePosition); + m_lastItem = m_lastClickedItem; + inspector()->setSelectedItems(QList<QQuickItem*>() << m_lastClickedItem); +} + +QQuickViewInspector *InspectTool::inspector() const +{ + return static_cast<QQuickViewInspector*>(AbstractTool::inspector()); +} + } // namespace QtQuick2 } // namespace QmlJSDebugger diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/zoomtool.h b/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.h index 02ed8e09df..25d0c634b2 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/zoomtool.h +++ b/src/plugins/qmltooling/qmldbg_qtquick2/inspecttool.h @@ -39,12 +39,14 @@ ** ****************************************************************************/ -#ifndef ZOOMTOOL_H -#define ZOOMTOOL_H +#ifndef INSPECTTOOL_H +#define INSPECTTOOL_H #include "abstracttool.h" #include <QtCore/QPointF> +#include <QtCore/QPointer> +#include <QtCore/QTimer> QT_FORWARD_DECLARE_CLASS(QQuickView) QT_FORWARD_DECLARE_CLASS(QQuickItem) @@ -53,46 +55,59 @@ namespace QmlJSDebugger { namespace QtQuick2 { class QQuickViewInspector; +class HoverHighlight; -class ZoomTool : public AbstractTool +class InspectTool : public AbstractTool { Q_OBJECT - public: enum ZoomDirection { ZoomIn, ZoomOut }; - explicit ZoomTool(QQuickViewInspector *inspector, QQuickView *view); - virtual ~ZoomTool(); - void leaveEvent(QEvent *) {} + InspectTool(QQuickViewInspector *inspector, QQuickView *view); + ~InspectTool(); + + void enable(bool enable); - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - void mouseReleaseEvent(QMouseEvent *) {} - void mouseDoubleClickEvent(QMouseEvent *event); + void leaveEvent(QEvent *); - void hoverMoveEvent(QMouseEvent *event); - void wheelEvent(QWheelEvent *event); + void mousePressEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void mouseReleaseEvent(QMouseEvent *); + void mouseDoubleClickEvent(QMouseEvent *); + + void hoverMoveEvent(QMouseEvent *); + void wheelEvent(QWheelEvent *); void keyPressEvent(QKeyEvent *) {} - void keyReleaseEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *); void touchEvent(QTouchEvent *event); private: + QQuickViewInspector *inspector() const; qreal nextZoomScale(ZoomDirection direction); void scaleView(const qreal &factor, const QPointF &newcenter, const QPointF &oldcenter); - void zoomTo100(); void zoomIn(); void zoomOut(); + void initializeDrag(const QPointF &pos); + void dragItemToPosition(); + void moveItem(bool valid); + void selectNextItem(); + void selectItem(); + +private slots: + void zoomTo100(); private: bool m_originalSmooth; bool m_dragStarted; bool m_pinchStarted; - QQuickItem *m_rootItem; + bool m_didPressAndHold; + bool m_tapEvent; + QPointer<QQuickItem> m_rootItem; QPointF m_adjustedOrigin; QPointF m_dragStartPosition; QPointF m_mousePosition; @@ -101,11 +116,16 @@ private: qreal m_smoothScaleFactor; qreal m_minScale; qreal m_maxScale; - qreal m_tapScaleCounter; qreal m_originalScale; + ulong m_touchTimestamp; + QTimer m_pressAndHoldTimer; + + HoverHighlight *m_hoverHighlight; + QQuickItem *m_lastItem; + QQuickItem *m_lastClickedItem; }; } // namespace QtQuick2 } // namespace QmlJSDebugger -#endif // ZOOMTOOL_H +#endif // INSPECTTOOL_H diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro b/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro index 33bda11dcc..f40e3f0b04 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro +++ b/src/plugins/qmltooling/qmldbg_qtquick2/qmldbg_qtquick2.pro @@ -12,22 +12,20 @@ INCLUDEPATH *= $$PWD $$PWD/../shared SOURCES += \ qtquick2plugin.cpp \ highlight.cpp \ - selectiontool.cpp \ qquickviewinspector.cpp \ ../shared/abstracttool.cpp \ ../shared/abstractviewinspector.cpp \ - zoomtool.cpp + inspecttool.cpp HEADERS += \ qtquick2plugin.h \ highlight.h \ - selectiontool.h \ qquickviewinspector.h \ ../shared/abstracttool.h \ ../shared/abstractviewinspector.h \ ../shared/qqmlinspectorprotocol.h \ ../shared/qmlinspectorconstants.h \ - zoomtool.h + inspecttool.h OTHER_FILES += qtquick2plugin.json diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.cpp b/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.cpp index 0a73540b40..95ce86678c 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.cpp +++ b/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.cpp @@ -43,8 +43,7 @@ #include "qqmlinspectorprotocol.h" #include "highlight.h" -#include "selectiontool.h" -#include "zoomtool.h" +#include "inspecttool.h" #include <QtQuick/private/qquickitem_p.h> @@ -120,8 +119,7 @@ QQuickViewInspector::QQuickViewInspector(QQuickView *view, QObject *parent) : AbstractViewInspector(parent), m_view(view), m_overlay(new QQuickItem), - m_selectionTool(new SelectionTool(this)), - m_zoomTool(0), + m_inspectTool(new InspectTool(this, view)), m_designMode(true) { // Try to make sure the overlay is always on top @@ -131,7 +129,7 @@ QQuickViewInspector::QQuickViewInspector(QQuickView *view, QObject *parent) : m_overlay->setParentItem(root); view->installEventFilter(this); - setCurrentTool(m_selectionTool); + setCurrentTool(m_inspectTool); } void QQuickViewInspector::changeCurrentObjects(const QList<QObject*> &objects) @@ -165,23 +163,13 @@ void QQuickViewInspector::reparentQmlObject(QObject *object, QObject *newParent) void QQuickViewInspector::changeTool(InspectorProtocol::Tool tool) { switch (tool) { - case InspectorProtocol::ColorPickerTool: - // TODO - emit colorPickerActivated(); - break; case InspectorProtocol::SelectMarqueeTool: // TODO emit marqueeSelectToolActivated(); break; - case InspectorProtocol::SelectTool: - setCurrentTool(m_selectionTool); - emit selectToolActivated(); - break; - case InspectorProtocol::ZoomTool: - if (!m_zoomTool) - m_zoomTool = new ZoomTool(this, m_view); - setCurrentTool(m_zoomTool); - emit zoomToolActivated(); + case InspectorProtocol::InspectTool: + setCurrentTool(m_inspectTool); + emit inspectToolActivated(); break; } } diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.h b/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.h index e8fdf351de..0fd2948279 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.h +++ b/src/plugins/qmltooling/qmldbg_qtquick2/qquickviewinspector.h @@ -55,9 +55,8 @@ QT_END_NAMESPACE namespace QmlJSDebugger { namespace QtQuick2 { -class SelectionTool; +class InspectTool; class SelectionHighlight; -class ZoomTool; class QQuickViewInspector : public AbstractViewInspector { @@ -99,8 +98,7 @@ private: QQuickView *m_view; QQuickItem *m_overlay; - SelectionTool *m_selectionTool; - ZoomTool *m_zoomTool; + InspectTool *m_inspectTool; QList<QWeakPointer<QQuickItem> > m_selectedItems; QHash<QQuickItem*, SelectionHighlight*> m_highlightItems; diff --git a/src/plugins/qmltooling/shared/abstracttool.h b/src/plugins/qmltooling/shared/abstracttool.h index 7e5ba65fd4..ef9894a852 100644 --- a/src/plugins/qmltooling/shared/abstracttool.h +++ b/src/plugins/qmltooling/shared/abstracttool.h @@ -64,6 +64,8 @@ public: AbstractViewInspector *inspector() const { return m_inspector; } + virtual void enable(bool enable) = 0; + virtual void leaveEvent(QEvent *event) = 0; virtual void mousePressEvent(QMouseEvent *event) = 0; diff --git a/src/plugins/qmltooling/shared/abstractviewinspector.cpp b/src/plugins/qmltooling/shared/abstractviewinspector.cpp index 135da1b8b7..60a95986b0 100644 --- a/src/plugins/qmltooling/shared/abstractviewinspector.cpp +++ b/src/plugins/qmltooling/shared/abstractviewinspector.cpp @@ -100,6 +100,7 @@ void AbstractViewInspector::setDesignModeBehavior(bool value) return; m_designModeBehavior = value; + m_currentTool->enable(m_designModeBehavior); emit designModeBehaviorChanged(value); sendDesignModeBehavior(value); } @@ -163,19 +164,9 @@ void AbstractViewInspector::setShowAppOnTop(bool appOnTop) emit showAppOnTopChanged(appOnTop); } -void AbstractViewInspector::changeToColorPickerTool() +void AbstractViewInspector::changeToInspectTool() { - changeTool(InspectorProtocol::ColorPickerTool); -} - -void AbstractViewInspector::changeToZoomTool() -{ - changeTool(InspectorProtocol::ZoomTool); -} - -void AbstractViewInspector::changeToSingleSelectTool() -{ - changeTool(InspectorProtocol::SelectTool); + changeTool(InspectorProtocol::InspectTool); } void AbstractViewInspector::changeToMarqueeSelectTool() @@ -272,18 +263,12 @@ bool AbstractViewInspector::keyReleaseEvent(QKeyEvent *event) { switch (event->key()) { case Qt::Key_V: - changeTool(InspectorProtocol::SelectTool); + changeTool(InspectorProtocol::InspectTool); break; // disabled because multiselection does not do anything useful without design mode // case Qt::Key_M: // changeTool(InspectorProtocol::SelectMarqueeTool); // break; - case Qt::Key_I: - changeTool(InspectorProtocol::ColorPickerTool); - break; - case Qt::Key_Z: - changeTool(InspectorProtocol::ZoomTool); - break; case Qt::Key_Space: setAnimationPaused(!animationPaused()); break; @@ -332,8 +317,8 @@ void AbstractViewInspector::handleMessage(const QByteArray &message) if (QObject *obj = QQmlDebugService::objectForId(debugId)) selectedObjects << obj; } - - changeCurrentObjects(selectedObjects); + if (m_designModeBehavior) + changeCurrentObjects(selectedObjects); break; } case InspectorProtocol::Reload: { @@ -497,17 +482,6 @@ void AbstractViewInspector::sendShowAppOnTop(bool showAppOnTop) m_debugService->sendMessage(message); } -void AbstractViewInspector::sendColorChanged(const QColor &color) -{ - QByteArray message; - QDataStream ds(&message, QIODevice::WriteOnly); - - ds << InspectorProtocol::ColorChanged - << color; - - m_debugService->sendMessage(message); -} - QString AbstractViewInspector::idStringForObject(QObject *obj) const { const int id = QQmlDebugService::idForObject(obj); diff --git a/src/plugins/qmltooling/shared/abstractviewinspector.h b/src/plugins/qmltooling/shared/abstractviewinspector.h index 04ca02917e..17f9e26660 100644 --- a/src/plugins/qmltooling/shared/abstractviewinspector.h +++ b/src/plugins/qmltooling/shared/abstractviewinspector.h @@ -45,7 +45,6 @@ #include <QtCore/QHash> #include <QtCore/QObject> #include <QtCore/QStringList> -#include <QtGui/QColor> #include "qqmlinspectorprotocol.h" #include "qmlinspectorconstants.h" @@ -109,10 +108,7 @@ signals: void showAppOnTopChanged(bool showAppOnTop); void reloadRequested(); void marqueeSelectToolActivated(); - void selectToolActivated(); - void zoomToolActivated(); - void colorPickerActivated(); - void selectedColorChanged(const QColor &color); + void inspectToolActivated(); void animationSpeedChanged(qreal factor); void animationPausedChanged(bool paused); @@ -132,15 +128,11 @@ protected: virtual bool wheelEvent(QWheelEvent *event); virtual bool touchEvent(QTouchEvent *event); -private slots: - void sendColorChanged(const QColor &color); - private: void sendDesignModeBehavior(bool inDesignMode); - void changeToColorPickerTool(); void changeToZoomTool(); - void changeToSingleSelectTool(); + void changeToInspectTool(); void changeToMarqueeSelectTool(); virtual void setDesignModeBehavior(bool value); diff --git a/src/plugins/qmltooling/shared/qmlinspectorconstants.h b/src/plugins/qmltooling/shared/qmlinspectorconstants.h index e5a0ee5450..4463283d9f 100644 --- a/src/plugins/qmltooling/shared/qmlinspectorconstants.h +++ b/src/plugins/qmltooling/shared/qmlinspectorconstants.h @@ -53,13 +53,10 @@ enum DesignTool { MarqueeSelectionToolMode = 2, MoveToolMode = 3, ResizeToolMode = 4, - ColorPickerMode = 5, ZoomMode = 6 }; -static const int DragStartTime = 50; - -static const int DragStartDistance = 20; +static const int PressAndHoldTimeout = 800; static const double ZoomSnapDelta = 0.04; diff --git a/src/plugins/qmltooling/shared/qqmlinspectorprotocol.h b/src/plugins/qmltooling/shared/qqmlinspectorprotocol.h index 63772aa8e4..8527bf6394 100644 --- a/src/plugins/qmltooling/shared/qqmlinspectorprotocol.h +++ b/src/plugins/qmltooling/shared/qqmlinspectorprotocol.h @@ -60,7 +60,6 @@ public: AnimationPausedChanged = 19, // highest value ChangeTool = 1, ClearComponentCache = 2, - ColorChanged = 3, CreateObject = 5, CurrentObjectsChanged = 6, DestroyObject = 7, @@ -77,10 +76,8 @@ public: }; enum Tool { - ColorPickerTool, - SelectMarqueeTool, - SelectTool, - ZoomTool + SelectMarqueeTool = 1, + InspectTool }; static inline QString toString(Message message) diff --git a/src/qml/animations/qanimationgroupjob.cpp b/src/qml/animations/qanimationgroupjob.cpp index 83b2192313..59bceaaf4e 100644 --- a/src/qml/animations/qanimationgroupjob.cpp +++ b/src/qml/animations/qanimationgroupjob.cpp @@ -51,8 +51,7 @@ QAnimationGroupJob::QAnimationGroupJob() QAnimationGroupJob::~QAnimationGroupJob() { - while (firstChild() != 0) - delete firstChild(); + clear(); } void QAnimationGroupJob::topLevelAnimationLoopChanged() @@ -123,9 +122,16 @@ void QAnimationGroupJob::removeAnimation(QAbstractAnimationJob *animation) void QAnimationGroupJob::clear() { - //### should this remove and delete, or just remove? - while (firstChild() != 0) - delete firstChild(); //removeAnimation(firstChild()); + QAbstractAnimationJob *child = firstChild(); + QAbstractAnimationJob *nextSibling = 0; + while (child != 0) { + child->m_group = 0; + nextSibling = child->nextSibling(); + delete child; + child = nextSibling; + } + m_firstChild = 0; + m_lastChild = 0; } void QAnimationGroupJob::resetUncontrolledAnimationsFinishTime() diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h index 318e0bd71f..115f3cbc3a 100644 --- a/src/qml/debugger/qqmldebug.h +++ b/src/qml/debugger/qqmldebug.h @@ -55,9 +55,9 @@ struct Q_QML_EXPORT QQmlDebuggingEnabler }; // Execute code in constructor before first QQmlEngine is instantiated -#if defined(QT_DECLARATIVE_DEBUG_NO_WARNING) +#if defined(QT_QML_DEBUG_NO_WARNING) static QQmlDebuggingEnabler qmlEnableDebuggingHelper(false); -#elif defined(QT_DECLARATIVE_DEBUG) +#elif defined(QT_QML_DEBUG) || defined(QT_DECLARATIVE_DEBUG) static QQmlDebuggingEnabler qmlEnableDebuggingHelper(true); #endif diff --git a/src/qml/debugger/qqmldebugserver.cpp b/src/qml/debugger/qqmldebugserver.cpp index dcf93b400e..468b653a1f 100644 --- a/src/qml/debugger/qqmldebugserver.cpp +++ b/src/qml/debugger/qqmldebugserver.cpp @@ -255,7 +255,7 @@ QQmlDebugServer *QQmlDebugServer::instance() commandLineTested = true; QCoreApplicationPrivate *appD = static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(qApp)); -#ifndef QQML_NO_DEBUG_PROTOCOL +#ifndef QT_QML_NO_DEBUGGER // ### remove port definition when protocol is changed int port = 0; bool block = false; @@ -398,7 +398,6 @@ void QQmlDebugServer::receiveMessage(const QByteArray &message) iter.value()->stateChanged(newState); } - qDebug("QML Debugger: Connection established."); d->messageArrivedCondition.wakeAll(); } else if (op == 1) { @@ -458,14 +457,14 @@ void QQmlDebugServerPrivate::_q_sendMessages(const QList<QByteArray> &messages) QList<QQmlDebugService*> QQmlDebugServer::services() const { - const Q_D(QQmlDebugServer); + Q_D(const QQmlDebugServer); QReadLocker(&d->pluginsLock); return d->plugins.values(); } QStringList QQmlDebugServer::serviceNames() const { - const Q_D(QQmlDebugServer); + Q_D(const QQmlDebugServer); QReadLocker(&d->pluginsLock); return d->plugins.keys(); } diff --git a/src/qml/debugger/qqmlenginedebugservice.cpp b/src/qml/debugger/qqmlenginedebugservice.cpp index cd09b69e48..d8997fd587 100644 --- a/src/qml/debugger/qqmlenginedebugservice.cpp +++ b/src/qml/debugger/qqmlenginedebugservice.cpp @@ -67,7 +67,7 @@ QQmlEngineDebugService *QQmlEngineDebugService::instance() } QQmlEngineDebugService::QQmlEngineDebugService(QObject *parent) - : QQmlDebugService(QStringLiteral("QmlDebugger"), 1, parent), + : QQmlDebugService(QStringLiteral("QmlDebugger"), 2, parent), m_watch(new QQmlWatcher(this)), m_statesDelegate(0) { @@ -86,7 +86,8 @@ QDataStream &operator<<(QDataStream &ds, const QQmlEngineDebugService::QQmlObjectData &data) { ds << data.url << data.lineNumber << data.columnNumber << data.idString - << data.objectName << data.objectType << data.objectId << data.contextId; + << data.objectName << data.objectType << data.objectId << data.contextId + << data.parentId; return ds; } @@ -94,7 +95,8 @@ QDataStream &operator>>(QDataStream &ds, QQmlEngineDebugService::QQmlObjectData &data) { ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString - >> data.objectName >> data.objectType >> data.objectId >> data.contextId; + >> data.objectName >> data.objectType >> data.objectId >> data.contextId + >> data.parentId; return ds; } @@ -264,7 +266,7 @@ void QQmlEngineDebugService::buildObjectDump(QDataStream &message, QQmlObjectProperty prop; prop.type = QQmlObjectProperty::SignalProperty; prop.hasNotifySignal = false; - QQmlExpression *expr = signalHandler->expression(); + QQmlBoundSignalExpression *expr = signalHandler->expression(); if (expr) { prop.value = expr->expression(); QObject *scope = expr->scopeObject(); @@ -376,7 +378,7 @@ QQmlEngineDebugService::objectData(QObject *object) rv.objectName = object->objectName(); rv.objectId = QQmlDebugService::idForObject(object); rv.contextId = QQmlDebugService::idForObject(qmlContext(object)); - + rv.parentId = QQmlDebugService::idForObject(object->parent()); QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); if (type) { QString typeName = type->qmlTypeName(); @@ -603,9 +605,9 @@ bool QQmlEngineDebugService::setBinding(int objectId, if (isLiteralValue) { property.write(expression); } else if (hasValidSignal(object, propertyName)) { - QQmlExpression *qmlExpression = new QQmlExpression(context, object, expression.toString()); + QQmlBoundSignalExpression *qmlExpression = new QQmlBoundSignalExpression(QQmlContextData::get(context), object, expression.toString(), + false, filename, line, column); QQmlPropertyPrivate::setSignalExpression(property, qmlExpression); - qmlExpression->setSourceLocation(filename, line, column); } else if (property.isProperty()) { QQmlBinding *binding = new QQmlBinding(expression.toString(), false, object, QQmlContextData::get(context), filename, line, column);; binding->setTarget(property); diff --git a/src/qml/debugger/qqmlenginedebugservice_p.h b/src/qml/debugger/qqmlenginedebugservice_p.h index 19a5776e27..3b855cb602 100644 --- a/src/qml/debugger/qqmlenginedebugservice_p.h +++ b/src/qml/debugger/qqmlenginedebugservice_p.h @@ -83,6 +83,7 @@ public: QString objectType; int objectId; int contextId; + int parentId; }; struct QQmlObjectProperty { diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp index c643073308..7109286197 100644 --- a/src/qml/debugger/qqmlprofilerservice.cpp +++ b/src/qml/debugger/qqmlprofilerservice.cpp @@ -195,7 +195,7 @@ void QQmlProfilerService::rangeData(RangeType range, const QUrl &rData) if (!QQmlDebugService::isDebuggingEnabled() || !m_enabled) return; - QQmlProfilerData rd = {m_timer.nsecsElapsed(), (int)RangeData, (int)range, rData.toString(QUrl::FormattingOption(0x100)), -1, -1, 0, 0}; + QQmlProfilerData rd = {m_timer.nsecsElapsed(), (int)RangeData, (int)range, rData.toString(), -1, -1, 0, 0}; processMessage(rd); } @@ -213,7 +213,7 @@ void QQmlProfilerService::rangeLocation(RangeType range, const QUrl &fileName, i if (!QQmlDebugService::isDebuggingEnabled() || !m_enabled) return; - QQmlProfilerData rd = {m_timer.nsecsElapsed(), (int)RangeLocation, (int)range, fileName.toString(QUrl::FormattingOption(0x100)), line, column, 0, 0}; + QQmlProfilerData rd = {m_timer.nsecsElapsed(), (int)RangeLocation, (int)range, fileName.toString(), line, column, 0, 0}; processMessage(rd); } diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h index 2e351792cc..8662abb52e 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/qml/debugger/qqmlprofilerservice_p.h @@ -54,7 +54,7 @@ // #include <private/qqmldebugservice_p.h> -#include "qqmlexpression.h" +#include <private/qqmlboundsignal_p.h> #include <QtCore/qelapsedtimer.h> #include <QtCore/qmetaobject.h> @@ -201,20 +201,20 @@ struct QQmlBindingProfiler { }; struct QQmlHandlingSignalProfiler { - QQmlHandlingSignalProfiler(const QMetaMethod &signal, QQmlExpression *expression) + QQmlHandlingSignalProfiler(const QMetaMethod &signal, QQmlBoundSignalExpression *expression) { enabled = QQmlProfilerService::instance ? QQmlProfilerService::instance->profilingEnabled() : false; - if (enabled) - init(signal, expression); - } - - QQmlHandlingSignalProfiler(QObject *object, int index, QQmlExpression *expression) - { - enabled = QQmlProfilerService::instance - ? QQmlProfilerService::instance->profilingEnabled() : false; - if (enabled) - init(object->metaObject()->method(index), expression); + if (enabled) { + QQmlProfilerService *service = QQmlProfilerService::instance; + service->startRange(QQmlProfilerService::HandlingSignal); + service->rangeData(QQmlProfilerService::HandlingSignal, + QString::fromLatin1(signal.methodSignature()) + QLatin1String(": ") + + expression->expression()); + service->rangeLocation(QQmlProfilerService::HandlingSignal, + expression->sourceFile(), expression->lineNumber(), + expression->columnNumber()); + } } ~QQmlHandlingSignalProfiler() @@ -224,19 +224,6 @@ struct QQmlHandlingSignalProfiler { } bool enabled; - -private: - void init(const QMetaMethod &signal, QQmlExpression *expression) - { - QQmlProfilerService *service = QQmlProfilerService::instance; - service->startRange(QQmlProfilerService::HandlingSignal); - service->rangeData(QQmlProfilerService::HandlingSignal, - QString::fromLatin1(signal.methodSignature()) + QLatin1String(": ") - + expression->expression()); - service->rangeLocation(QQmlProfilerService::HandlingSignal, - expression->sourceFile(), expression->lineNumber(), - expression->columnNumber()); - } }; struct QQmlObjectCreatingProfiler { diff --git a/src/qml/qml/qqmlabstractexpression_p.h b/src/qml/qml/qqmlabstractexpression_p.h index fe2ee1762b..1d5ecac08e 100644 --- a/src/qml/qml/qqmlabstractexpression_p.h +++ b/src/qml/qml/qqmlabstractexpression_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE -class QQmlAbstractExpression +class Q_QML_PRIVATE_EXPORT QQmlAbstractExpression { public: QQmlAbstractExpression(); diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h index 8e67a58511..7558ab3e35 100644 --- a/src/qml/qml/qqmlaccessors_p.h +++ b/src/qml/qml/qqmlaccessors_p.h @@ -47,7 +47,7 @@ #include <QtCore/qhash.h> #include <QtCore/QReadWriteLock> -#ifdef Q_OS_QNX +#if defined(Q_OS_QNX) || defined(Q_OS_LINUX) #include <stdint.h> #endif diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 3a3bf403e6..b77484c7e5 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -49,6 +49,7 @@ #include "qqml.h" #include "qqmlcontext.h" #include "qqmlglobal_p.h" +#include "qqmlrewrite_p.h" #include <private/qqmlprofilerservice_p.h> #include <private/qv8debugservice_p.h> @@ -59,6 +60,105 @@ Q_DECLARE_METATYPE(QJSValue) QT_BEGIN_NAMESPACE +static QQmlJavaScriptExpression::VTable QQmlBoundSignalExpression_jsvtable = { + QQmlBoundSignalExpression::expressionIdentifier, + QQmlBoundSignalExpression::expressionChanged +}; + +QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression, + bool isRewritten, const QString &fileName, int line, int column) + : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable) +{ + setNotifyOnValueChanged(false); + setContext(ctxt); + setScopeObject(scope); + m_expression = QString::fromUtf8(expression); + m_expressionFunctionValid = false; + m_expressionFunctionRewritten = isRewritten; + m_fileName = fileName; + m_line = line; + m_column = column; +} + +QQmlBoundSignalExpression::QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression, + bool isRewritten, const QString &fileName, int line, int column) + : QQmlJavaScriptExpression(&QQmlBoundSignalExpression_jsvtable) +{ + setNotifyOnValueChanged(false); + setContext(ctxt); + setScopeObject(scope); + m_expression = expression; + m_expressionFunctionValid = false; + m_expressionFunctionRewritten = isRewritten; + m_fileName = fileName; + m_line = line; + m_column = column; +} + +QQmlBoundSignalExpression::~QQmlBoundSignalExpression() +{ + qPersistentDispose(m_v8function); + qPersistentDispose(m_v8qmlscope); +} + +QString QQmlBoundSignalExpression::expressionIdentifier(QQmlJavaScriptExpression *e) +{ + QQmlBoundSignalExpression *This = static_cast<QQmlBoundSignalExpression *>(e); + return QLatin1String("\"") + This->m_expression + QLatin1String("\""); +} + +void QQmlBoundSignalExpression::expressionChanged(QQmlJavaScriptExpression *) +{ + // bound signals do not notify on change. +} + +// This mirrors code in QQmlExpressionPrivate::value() and v8value(). +// Any change made here should be made there and vice versa. +void QQmlBoundSignalExpression::evaluate(QObject *secondaryScope) +{ + Q_ASSERT (context() && engine()); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); + + ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. + { + v8::HandleScope handle_scope; + v8::Context::Scope context_scope(ep->v8engine()->context()); + if (!m_expressionFunctionValid) { + bool ok = true; + QString code; + if (m_expressionFunctionRewritten) { + code = m_expression; + } else { + QQmlRewrite::RewriteSignalHandler rewriteSignalHandler; + code = rewriteSignalHandler(m_expression, m_functionName, &ok); + } + + if (ok) + m_v8function = evalFunction(context(), scopeObject(), code, m_fileName, m_line, &m_v8qmlscope); + + if (m_v8function.IsEmpty() || m_v8function->IsNull()) { + ep->dereferenceScarceResources(); + return; // could not evaluate function. Not valid. + } + + setUseSharedContext(false); + m_expressionFunctionValid = true; + } + + if (secondaryScope) { + QObject *restoreSecondaryScope = 0; + restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, secondaryScope); + QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0); + ep->v8engine()->contextWrapper()->setSecondaryScope(m_v8qmlscope, restoreSecondaryScope); + } else { + QQmlJavaScriptExpression::evaluate(context(), m_v8function, 0); + } + } + ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. +} + +//////////////////////////////////////////////////////////////////////// + class QQmlBoundSignalParameters : public QObject { Q_OBJECT @@ -104,10 +204,9 @@ QQmlAbstractBoundSignal::~QQmlAbstractBoundSignal() } } -void QQmlAbstractBoundSignal::addToObject() +void QQmlAbstractBoundSignal::addToObject(QObject *obj) { Q_ASSERT(!m_prevSignal); - QObject *obj = object(); Q_ASSERT(obj); QQmlData *data = QQmlData::get(obj, true); @@ -120,13 +219,15 @@ void QQmlAbstractBoundSignal::addToObject() QQmlBoundSignal::QQmlBoundSignal(QObject *scope, const QMetaMethod &signal, QObject *owner) -: m_expression(0), m_signal(signal), m_paramsValid(false), m_isEvaluating(false), m_params(0), m_owner(owner) +: m_expression(0), m_params(0), m_scope(scope), m_signal(signal), m_paramsValid(false), m_isEvaluating(false) { // This is thread safe. Although it may be updated by two threads, they // will both set it to the same value - so the worst thing that can happen // is that they both do the work to figure it out. Boo hoo. if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); + addToObject(owner); + QQmlPropertyPrivate::connect(scope, m_signal.methodIndex(), this, evaluateIdx); } @@ -144,7 +245,7 @@ int QQmlBoundSignal::index() const /*! Returns the signal expression. */ -QQmlExpression *QQmlBoundSignal::expression() const +QQmlBoundSignalExpression *QQmlBoundSignal::expression() const { return m_expression; } @@ -156,9 +257,9 @@ QQmlExpression *QQmlBoundSignal::expression() const The QQmlBoundSignal instance takes ownership of \a e. The caller is assumes ownership of the returned QQmlExpression. */ -QQmlExpression *QQmlBoundSignal::setExpression(QQmlExpression *e) +QQmlBoundSignalExpression *QQmlBoundSignal::setExpression(QQmlBoundSignalExpression *e) { - QQmlExpression *rv = m_expression; + QQmlBoundSignalExpression *rv = m_expression; m_expression = e; if (m_expression) m_expression->setNotifyOnValueChanged(false); return rv; @@ -183,8 +284,8 @@ int QQmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) } if (m_params) m_params->setValues(a); - if (m_expression && m_expression->engine()) { - QQmlExpressionPrivate::get(m_expression)->value(m_params); + if (m_expression && m_expression->context() && m_expression->engine()) { + m_expression->evaluate(m_params); if (m_expression && m_expression->hasError()) QQmlEnginePrivate::warning(m_expression->engine(), m_expression->error()); } @@ -247,7 +348,7 @@ QQmlBoundSignalParameters::QQmlBoundSignalParameters(const QMetaMethod &method, if (scope == "Qt") meta = &QObject::staticQtMetaObject; else - meta = static_cast<QQmlBoundSignal*>(parent)->object()->metaObject(); + meta = static_cast<QQmlBoundSignal*>(parent)->scope()->metaObject(); for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { QMetaEnum m = meta->enumerator(i); if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) { @@ -300,71 +401,6 @@ int QQmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a) } } -//////////////////////////////////////////////////////////////////////// - -QQmlBoundSignalNoParams::QQmlBoundSignalNoParams(QObject *scope, const QMetaMethod &signal, - QObject *owner) -: m_expression(0), m_owner(owner), m_index(signal.methodIndex()), m_isEvaluating(false) -{ - callback = &subscriptionCallback; - QQmlNotifierEndpoint::connect(scope, m_index); -} - -QQmlBoundSignalNoParams::~QQmlBoundSignalNoParams() -{ - delete m_expression; - m_expression = 0; -} - -int QQmlBoundSignalNoParams::index() const -{ - return m_index; -} - -/*! - Returns the signal expression. -*/ -QQmlExpression *QQmlBoundSignalNoParams::expression() const -{ - return m_expression; -} - -/*! - Sets the signal expression to \a e. Returns the current signal expression, - or null if there is no signal expression. - - The QQmlBoundSignalNoParams instance takes ownership of \a e. The caller is - assumes ownership of the returned QQmlExpression. -*/ -QQmlExpression *QQmlBoundSignalNoParams::setExpression(QQmlExpression *e) -{ - QQmlExpression *rv = m_expression; - m_expression = e; - if (m_expression) m_expression->setNotifyOnValueChanged(false); - return rv; -} - -void QQmlBoundSignalNoParams::subscriptionCallback(QQmlNotifierEndpoint *e) -{ - QQmlBoundSignalNoParams *s = static_cast<QQmlBoundSignalNoParams*>(e); - if (!s->m_expression) - return; - - if (QQmlDebugService::isDebuggingEnabled()) - QV8DebugService::instance()->signalEmitted(QString::fromAscii(s->m_owner->metaObject()->method(s->m_index).methodSignature())); - - QQmlHandlingSignalProfiler prof(s->m_owner, s->m_index, s->m_expression); - - s->m_isEvaluating = true; - - if (s->m_expression && s->m_expression->engine()) { - QQmlExpressionPrivate::get(s->m_expression)->value(); - if (s->m_expression && s->m_expression->hasError()) - QQmlEnginePrivate::warning(s->m_expression->engine(), s->m_expression->error()); - } - s->m_isEvaluating = false; -} - QT_END_NAMESPACE #include <qqmlboundsignal.moc> diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h index 5fc8c3522f..c6ce875d07 100644 --- a/src/qml/qml/qqmlboundsignal_p.h +++ b/src/qml/qml/qqmlboundsignal_p.h @@ -53,15 +53,51 @@ // We mean it. // -#include "qqmlexpression.h" - #include <QtCore/qmetaobject.h> -#include <private/qqmlnotifier_p.h> +#include <private/qqmlabstractexpression_p.h> +#include <private/qqmljavascriptexpression_p.h> #include <private/qobject_p.h> QT_BEGIN_NAMESPACE +class Q_QML_PRIVATE_EXPORT QQmlBoundSignalExpression : public QQmlAbstractExpression, public QQmlJavaScriptExpression +{ +public: + QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QByteArray &expression, + bool isRewritten, const QString &fileName, int line, int column); + QQmlBoundSignalExpression(QQmlContextData *ctxt, QObject *scope, const QString &expression, + bool isRewritten, const QString &fileName, int line, int column); + ~QQmlBoundSignalExpression(); + + // "inherited" from QQmlJavaScriptExpression. + static QString expressionIdentifier(QQmlJavaScriptExpression *); + static void expressionChanged(QQmlJavaScriptExpression *); + + // evaluation of a bound signal expression doesn't return any value + void evaluate(QObject *secondaryScope = 0); + + QString sourceFile() const { return m_fileName; } + int lineNumber() const { return m_line; } + int columnNumber() const { return m_column; } + QString expression() const { return m_expression; } + + QQmlEngine *engine() const { return context() ? context()->engine : 0; } + +private: + v8::Persistent<v8::Object> m_v8qmlscope; + v8::Persistent<v8::Function> m_v8function; + + QString m_expression; + QString m_functionName; // hint for debugger + QString m_fileName; + int m_line; + int m_column; + + bool m_expressionFunctionValid:1; + bool m_expressionFunctionRewritten:1; +}; + class Q_QML_EXPORT QQmlAbstractBoundSignal { public: @@ -69,11 +105,12 @@ public: virtual ~QQmlAbstractBoundSignal(); virtual int index() const = 0; - virtual QQmlExpression *expression() const = 0; - virtual QQmlExpression *setExpression(QQmlExpression *) = 0; - virtual QObject *object() = 0; + virtual QQmlBoundSignalExpression *expression() const = 0; + virtual QQmlBoundSignalExpression *setExpression(QQmlBoundSignalExpression *) = 0; + virtual QObject *scope() = 0; - void addToObject(); +protected: + void addToObject(QObject *owner); private: friend class QQmlData; @@ -93,9 +130,9 @@ public: int index() const; - QQmlExpression *expression() const; - QQmlExpression *setExpression(QQmlExpression *); - QObject *object() { return m_owner; } + QQmlBoundSignalExpression *expression() const; + QQmlBoundSignalExpression *setExpression(QQmlBoundSignalExpression *); + QObject *scope() { return m_scope; } bool isEvaluating() const { return m_isEvaluating; } @@ -103,39 +140,14 @@ protected: virtual int qt_metacall(QMetaObject::Call c, int id, void **a); private: - QQmlExpression *m_expression; + QQmlBoundSignalExpression *m_expression; + QQmlBoundSignalParameters *m_params; + QObject *m_scope; QMetaMethod m_signal; bool m_paramsValid : 1; bool m_isEvaluating : 1; - QQmlBoundSignalParameters *m_params; - QObject *m_owner; -}; - -class Q_QML_EXPORT QQmlBoundSignalNoParams : public QQmlAbstractBoundSignal, - public QQmlNotifierEndpoint -{ -public: - QQmlBoundSignalNoParams(QObject *scope, const QMetaMethod &signal, QObject *owner); - virtual ~QQmlBoundSignalNoParams(); - - int index() const; - - QQmlExpression *expression() const; - QQmlExpression *setExpression(QQmlExpression *); - QObject *object() { return m_owner; } - - static void subscriptionCallback(QQmlNotifierEndpoint *e); - - bool isEvaluating() const { return m_isEvaluating; } - -private: - QQmlExpression *m_expression; - QObject *m_owner; - int m_index; - bool m_isEvaluating; }; - QT_END_NAMESPACE #endif // QQMLBOUNDSIGNAL_P_H diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index dec9911481..54577f9b92 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -3468,6 +3468,7 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, store.value = js.compiledIndex; store.context = js.bindingContext.stack; store.owner = js.bindingContext.owner; + store.isAlias = prop->isAlias; if (valueTypeProperty) { store.isRoot = (compileState->root == valueTypeProperty->parent); } else { @@ -3488,30 +3489,29 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, } else if (ref.dataType == BindingReference::QtScript) { const JSBindingReference &js = static_cast<const JSBindingReference &>(ref); - QQmlInstruction store; - store.assignBinding.value = output->indexForString(js.rewrittenExpression); - store.assignBinding.context = js.bindingContext.stack; - store.assignBinding.owner = js.bindingContext.owner; - store.assignBinding.line = binding->location.start.line; - store.assignBinding.column = binding->location.start.column; + Instruction::StoreBinding store; + store.value = output->indexForString(js.rewrittenExpression); + store.context = js.bindingContext.stack; + store.owner = js.bindingContext.owner; + store.line = binding->location.start.line; + store.column = binding->location.start.column; + store.isAlias = prop->isAlias; if (valueTypeProperty) { - store.assignBinding.isRoot = (compileState->root == valueTypeProperty->parent); + store.isRoot = (compileState->root == valueTypeProperty->parent); } else { - store.assignBinding.isRoot = (compileState->root == obj); + store.isRoot = (compileState->root == obj); } Q_ASSERT(js.bindingContext.owner == 0 || (js.bindingContext.owner != 0 && valueTypeProperty)); if (js.bindingContext.owner) { - store.assignBinding.property = genValueTypeData(prop, valueTypeProperty); + store.property = genValueTypeData(prop, valueTypeProperty); } else { - store.assignBinding.property = prop->core; + store.property = prop->core; } - output->addInstructionHelper( - !prop->isAlias ? QQmlInstruction::StoreBinding - : QQmlInstruction::StoreBindingOnAlias - , store); + + output->addInstruction(store); } else { Q_ASSERT(!"Unhandled BindingReference::DataType type"); } @@ -3588,8 +3588,7 @@ bool QQmlCompiler::completeComponentBuild() bool isSharable = false; binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable); - if (isSharable && !binding.property->isAlias /* See above re alias */ && - binding.property->type != qMetaTypeId<QQmlBinding*>()) { + if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) { binding.dataType = BindingReference::V8; sharedBindings.append(b); diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 7bddaadcd0..2e46192246 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1396,7 +1396,8 @@ void QV8IncubatorResource::statusChanged(Status s) { if (s == Ready) { Q_ASSERT(QQmlData::get(object())); - QQmlData::get(object())->setImplicitDestructible(); + QQmlData::get(object())->explicitIndestructibleSet = false; + QQmlData::get(object())->indestructible = false; } if (!me.IsEmpty()) { // Will be false in synchronous mode diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index e7e001c4f2..3dd1c3ccba 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -181,11 +181,25 @@ public: QQmlNotifier *objectNameNotifier() const; QHash<int, QObject *> *attachedProperties() const; + static inline bool wasDeleted(QObject *); private: // For objectNameNotifier and attachedProperties mutable QQmlDataExtended *extendedData; }; +bool QQmlData::wasDeleted(QObject *object) +{ + if (!object) + return true; + + QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); + if (priv->wasDeleted) + return true; + + return priv->declarativeData && + static_cast<QQmlData *>(priv->declarativeData)->isQueuedForDeletion; +} + QQmlNotifierEndpoint *QQmlData::notify(int index) { Q_ASSERT(index <= 0xFFFF); diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp index df5f7f8ee9..82289311c9 100644 --- a/src/qml/qml/qqmldirparser.cpp +++ b/src/qml/qml/qqmldirparser.cpp @@ -297,16 +297,16 @@ QList<QQmlDirParser::TypeInfo> QQmlDirParser::typeInfos() const QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component) { - return debug << qPrintable(QString("{%1 %2.%3}").arg(component.typeName) - .arg(component.majorVersion) - .arg(component.minorVersion)); + const QString output = QString::fromLatin1("{%1 %2.%3}"). + arg(component.typeName).arg(component.majorVersion).arg(component.minorVersion); + return debug << qPrintable(output); } QDebug &operator<< (QDebug &debug, const QQmlDirParser::Script &script) { - return debug << qPrintable(QString("{%1 %2.%3}").arg(script.nameSpace) - .arg(script.majorVersion) - .arg(script.minorVersion)); + const QString output = QString::fromLatin1("{%1 %2.%3}"). + arg(script.nameSpace).arg(script.majorVersion).arg(script.minorVersion); + return debug << qPrintable(output); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 955c8ce414..153d6b325a 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -598,9 +598,17 @@ QQmlEngine::~QQmlEngine() /*! Clears the engine's internal component cache. - Normally the QQmlEngine caches components loaded from qml - files. This method clears this cache and forces the component to be - reloaded. + This function causes the property metadata of all components previously + loaded by the engine to be destroyed. All previously loaded components and + the property bindings for all extant objects created from those components will + cease to function. + + This function returns the engine to a state where it does not contain any loaded + component data. This may be useful in order to reload a smaller subset of the + previous component set, or to load a new version of a previously loaded component. + + Once the component cache has been cleared, components must be loaded before + any new objects can be created. */ void QQmlEngine::clearComponentCache() { diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index d760486605..940b3c8354 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -60,7 +60,7 @@ static QQmlJavaScriptExpression::VTable QQmlExpressionPrivate_jsvtable = { QQmlExpressionPrivate::QQmlExpressionPrivate() : QQmlJavaScriptExpression(&QQmlExpressionPrivate_jsvtable), expressionFunctionValid(true), expressionFunctionRewritten(false), - extractExpressionFromFunction(false), line(-1), dataRef(0) + line(-1) { } @@ -68,8 +68,6 @@ QQmlExpressionPrivate::~QQmlExpressionPrivate() { qPersistentDispose(v8qmlscope); qPersistentDispose(v8function); - if (dataRef) dataRef->release(); - dataRef = 0; } void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QObject *me) @@ -321,13 +319,7 @@ QQmlContext *QQmlExpression::context() const QString QQmlExpression::expression() const { Q_D(const QQmlExpression); - if (d->extractExpressionFromFunction && context()->engine()) { - QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(context()->engine()); - v8::HandleScope handle_scope; - v8::Context::Scope scope(v8engine->context()); - - return v8engine->toString(v8::Handle<v8::Value>(d->v8function)); - } else if (!d->expressionUtf8.isEmpty()) { + if (!d->expressionUtf8.isEmpty()) { return QString::fromUtf8(d->expressionUtf8); } else { return d->expression; @@ -351,7 +343,7 @@ void QQmlExpression::setExpression(const QString &expression) } // Must be called with a valid handle scope -v8::Local<v8::Value> QQmlExpressionPrivate::v8value(QObject *secondaryScope, bool *isUndefined) +v8::Local<v8::Value> QQmlExpressionPrivate::v8value(bool *isUndefined) { if (!expressionFunctionValid) { bool ok = true; @@ -369,21 +361,10 @@ v8::Local<v8::Value> QQmlExpressionPrivate::v8value(QObject *secondaryScope, boo expressionFunctionValid = true; } - - if (secondaryScope) { - v8::Local<v8::Value> result; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); - QObject *restoreSecondaryScope = 0; - restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope); - result = evaluate(context(), v8function, isUndefined); - ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope); - return result; - } else { - return evaluate(context(), v8function, isUndefined); - } + return evaluate(context(), v8function, isUndefined); } -QVariant QQmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined) +QVariant QQmlExpressionPrivate::value(bool *isUndefined) { Q_Q(QQmlExpression); @@ -400,7 +381,7 @@ QVariant QQmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined { v8::HandleScope handle_scope; v8::Context::Scope context_scope(ep->v8engine()->context()); - v8::Local<v8::Value> result = v8value(secondaryScope, isUndefined); + v8::Local<v8::Value> result = v8value(isUndefined); rv = ep->v8engine()->toVariant(result, qMetaTypeId<QList<QObject*> >()); } @@ -421,7 +402,7 @@ QVariant QQmlExpressionPrivate::value(QObject *secondaryScope, bool *isUndefined QVariant QQmlExpression::evaluate(bool *valueIsUndefined) { Q_D(QQmlExpression); - return d->value(0, valueIsUndefined); + return d->value(valueIsUndefined); } /*! diff --git a/src/qml/qml/qqmlexpression_p.h b/src/qml/qml/qqmlexpression_p.h index 186e3aebf9..d32e2d314c 100644 --- a/src/qml/qml/qqmlexpression_p.h +++ b/src/qml/qml/qqmlexpression_p.h @@ -82,9 +82,9 @@ public: void init(QQmlContextData *, const QString &, bool, QObject *, const QString &, int, int); void init(QQmlContextData *, const QByteArray &, bool, QObject *, const QString &, int, int); - QVariant value(QObject *secondaryScope = 0, bool *isUndefined = 0); + QVariant value(bool *isUndefined = 0); - v8::Local<v8::Value> v8value(QObject *secondaryScope = 0, bool *isUndefined = 0); + v8::Local<v8::Value> v8value(bool *isUndefined = 0); static inline QQmlExpressionPrivate *get(QQmlExpression *expr); static inline QQmlExpression *get(QQmlExpressionPrivate *expr); @@ -96,7 +96,6 @@ public: bool expressionFunctionValid:1; bool expressionFunctionRewritten:1; - bool extractExpressionFromFunction:1; // "Inherited" from QQmlJavaScriptExpression static QString expressionIdentifier(QQmlJavaScriptExpression *); @@ -113,8 +112,6 @@ public: int line; int column; QString name; //function name, hint for the debugger - - QQmlRefCount *dataRef; }; QQmlExpressionPrivate *QQmlExpressionPrivate::get(QQmlExpression *expr) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 1224efdaac..be870cad4c 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -545,27 +545,27 @@ QString QQmlImportsPrivate::add(const QQmlDirComponents &qmldircomponentsnetwork } } - // TODO: Should this search be omitted if found == true? - - // step 2: search for extension with encoded version major - foreach (const QString &p, database->fileImportPath) { - dir = p+Slash+url; + if (!found) { + // step 2: search for extension with encoded version major + foreach (const QString &p, database->fileImportPath) { + dir = p+Slash+url; - QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir")); - const QString absoluteFilePath = fi.absoluteFilePath(); + QFileInfo fi(dir+QString(QLatin1String(".%1")).arg(vmaj)+QLatin1String("/qmldir")); + const QString absoluteFilePath = fi.absoluteFilePath(); - if (fi.isFile()) { - found = true; + if (fi.isFile()) { + found = true; - const QString absolutePath = fi.absolutePath(); - if (absolutePath.at(0) == QLatin1Char(':')) - url = QLatin1String("qrc://") + absolutePath.mid(1); - else - url = QUrl::fromLocalFile(fi.absolutePath()).toString(); - uri = resolvedUri(dir, database); - if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) - return QString(); - break; + const QString absolutePath = fi.absolutePath(); + if (absolutePath.at(0) == QLatin1Char(':')) + url = QLatin1String("qrc://") + absolutePath.mid(1); + else + url = QUrl::fromLocalFile(fi.absolutePath()).toString(); + uri = resolvedUri(dir, database); + if (!importExtension(absoluteFilePath, uri, database, &qmldircomponents, &qmldirscripts, errors)) + return QString(); + break; + } } } diff --git a/src/qml/qml/qqmlinstruction.cpp b/src/qml/qml/qqmlinstruction.cpp index b37117e69b..82cf235712 100644 --- a/src/qml/qml/qqmlinstruction.cpp +++ b/src/qml/qml/qqmlinstruction.cpp @@ -210,9 +210,6 @@ void QQmlCompiledData::dump(QQmlInstruction *instr, int idx) case QQmlInstruction::StoreBinding: qWarning().nospace() << idx << "\t\t" << "STORE_BINDING\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; break; - case QQmlInstruction::StoreBindingOnAlias: - qWarning().nospace() << idx << "\t\t" << "STORE_BINDING_ALIAS\t" << instr->assignBinding.property.coreIndex << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; - break; case QQmlInstruction::StoreV4Binding: qWarning().nospace() << idx << "\t\t" << "STORE_COMPILED_BINDING\t" << instr->assignV4Binding.property << "\t" << instr->assignV4Binding.value << "\t" << instr->assignV4Binding.context; break; diff --git a/src/qml/qml/qqmlinstruction_p.h b/src/qml/qml/qqmlinstruction_p.h index b7533aca68..cfc530df04 100644 --- a/src/qml/qml/qqmlinstruction_p.h +++ b/src/qml/qml/qqmlinstruction_p.h @@ -114,7 +114,6 @@ QT_BEGIN_NAMESPACE F(BeginObject, begin) \ F(InitV8Bindings, initV8Bindings) \ F(StoreBinding, assignBinding) \ - F(StoreBindingOnAlias, assignBinding) \ F(StoreV8Binding, assignBinding) \ F(StoreV4Binding, assignV4Binding) \ F(StoreValueSource, assignValueSource) \ @@ -251,6 +250,7 @@ union QQmlInstruction short context; short owner; bool isRoot; + bool isAlias; ushort line; ushort column; }; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index f68c345f3a..075c1f6c4f 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -920,7 +920,7 @@ QQmlPropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valu Returns the expression associated with this signal property, or 0 if no signal expression exists. */ -QQmlExpression * +QQmlBoundSignalExpression * QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) { if (!(that.type() & QQmlProperty::SignalProperty)) @@ -948,9 +948,9 @@ QQmlPropertyPrivate::signalExpression(const QQmlProperty &that) Ownership of \a expr transfers to QML. Ownership of the return value is assumed by the caller. */ -QQmlExpression * +QQmlBoundSignalExpression * QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, - QQmlExpression *expr) + QQmlBoundSignalExpression *expr) { if (!(that.type() & QQmlProperty::SignalProperty)) { delete expr; @@ -970,13 +970,8 @@ QQmlPropertyPrivate::setSignalExpression(const QQmlProperty &that, return signalHandler->setExpression(expr); if (expr) { - QQmlAbstractBoundSignal *signal = 0; - if (that.method().parameterTypes().count()) - signal = new QQmlBoundSignal(that.d->object, that.method(), that.d->object); - else - signal = new QQmlBoundSignalNoParams(that.d->object, that.method(), that.d->object); - QQmlExpression *oldExpr = signal->setExpression(expr); - signal->addToObject(); + QQmlBoundSignal *signal = new QQmlBoundSignal(that.d->object, that.method(), that.d->object); + QQmlBoundSignalExpression *oldExpr = signal->setExpression(expr); return oldExpr; } else { return 0; @@ -1531,13 +1526,29 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object, return true; const char *valueType = 0; - if (value.userType() == QVariant::Invalid) valueType = "null"; - else valueType = QMetaType::typeName(value.userType()); + const char *propertyType = 0; + + if (value.userType() == QMetaType::QObjectStar) { + if (QObject *o = *(QObject **)value.constData()) { + valueType = o->metaObject()->className(); + + if (const QMetaObject *propertyMetaObject = rawMetaObjectForType(QQmlEnginePrivate::get(engine), type)) { + propertyType = propertyMetaObject->className(); + } + } + } else if (value.userType() != QVariant::Invalid) { + valueType = QMetaType::typeName(value.userType()); + } + + if (!valueType) + valueType = "null"; + if (!propertyType) + propertyType = QMetaType::typeName(type); expression->delayedError()->error.setDescription(QLatin1String("Unable to assign ") + QLatin1String(valueType) + QLatin1String(" to ") + - QLatin1String(QMetaType::typeName(type))); + QLatin1String(propertyType)); return false; } diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index f4a9ced53b..e33c95ae41 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QQmlContext; -class QQmlExpression; +class QQmlBoundSignalExpression; class QQmlEnginePrivate; class QQmlJavaScriptExpression; class Q_QML_PRIVATE_EXPORT QQmlPropertyPrivate : public QQmlRefCount @@ -140,9 +140,9 @@ public: static QQmlAbstractBinding *setBinding(const QQmlProperty &that, QQmlAbstractBinding *, WriteFlags flags = DontRemoveBinding); - static QQmlExpression *signalExpression(const QQmlProperty &that); - static QQmlExpression *setSignalExpression(const QQmlProperty &that, - QQmlExpression *) ; + static QQmlBoundSignalExpression *signalExpression(const QQmlProperty &that); + static QQmlBoundSignalExpression *setSignalExpression(const QQmlProperty &that, + QQmlBoundSignalExpression *) ; static bool write(const QQmlProperty &that, const QVariant &, WriteFlags); static bool writeBinding(QObject *, const QQmlPropertyData &, QQmlContextData *context, diff --git a/src/qml/qml/qqmlrewrite.cpp b/src/qml/qml/qqmlrewrite.cpp index 72bd23955b..bbb17b631d 100644 --- a/src/qml/qml/qqmlrewrite.cpp +++ b/src/qml/qml/qqmlrewrite.cpp @@ -51,6 +51,8 @@ DEFINE_BOOL_CONFIG_OPTION(rewriteDump, QML_REWRITE_DUMP); namespace QQmlRewrite { +QString SharedBindingTester::evalString("eval"); + static void rewriteStringLiteral(AST::StringLiteral *ast, const QString *code, int startPosition, TextWriter *writer) { const unsigned position = ast->firstSourceLocation().begin() - startPosition + 1; @@ -419,6 +421,21 @@ QString RewriteSignalHandler::operator()(QQmlJS::AST::Node *node, const QString return rewritten; } +QString RewriteSignalHandler::operator()(const QString &code, const QString &name, bool *ok) +{ + Engine engine; + Lexer lexer(&engine); + Parser parser(&engine); + lexer.setCode(code, 0); + parser.parseStatement(); + if (!parser.statement()) { + if (ok) *ok = false; + return QString(); + } + if (ok) *ok = true; + return operator()(parser.statement(), code, name); +} + } // namespace QQmlRewrite QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlrewrite_p.h b/src/qml/qml/qqmlrewrite_p.h index 1d69839878..efa10ce316 100644 --- a/src/qml/qml/qqmlrewrite_p.h +++ b/src/qml/qml/qqmlrewrite_p.h @@ -70,9 +70,11 @@ public: bool isSharable(const QString &code); bool isSharable(AST::Node *Node); - virtual bool visit(AST::FunctionDeclaration *) { _sharable = false; return false; } - virtual bool visit(AST::FunctionExpression *) { _sharable = false; return false; } - virtual bool visit(AST::CallExpression *) { _sharable = false; return false; } + inline virtual bool visit(AST::FunctionDeclaration *); + inline virtual bool visit(AST::FunctionExpression *); + inline virtual bool visit(AST::IdentifierExpression *); + + static QString evalString; }; class RewriteBinding: protected AST::Visitor @@ -133,6 +135,7 @@ class RewriteSignalHandler: protected AST::Visitor public: RewriteSignalHandler(); QString operator()(QQmlJS::AST::Node *node, const QString &code, const QString &name); + QString operator()(const QString &code, const QString &name, bool *ok = 0); protected: void rewriteMultilineStrings(QString &code); @@ -142,6 +145,26 @@ protected: virtual bool visit(AST::StringLiteral *ast); }; +bool SharedBindingTester::visit(AST::FunctionDeclaration *) +{ + _sharable = false; + return false; +} + +bool SharedBindingTester::visit(AST::FunctionExpression *) +{ + _sharable = false; + return false; +} + +bool SharedBindingTester::visit(AST::IdentifierExpression *e) +{ + if (e->name == evalString) + _sharable = false; + + return false; // IdentifierExpression is a leaf node anyway +} + } // namespace QQmlRewrite QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp index e1925eb238..37c0fb44a4 100644 --- a/src/qml/qml/qqmlscript.cpp +++ b/src/qml/qml/qqmlscript.cpp @@ -66,6 +66,11 @@ QQmlScript::Object::Object() : type(-1), idIndex(-1), metatype(0), synthCache(0), defaultProperty(0), parserStatusCast(-1), componentCompileState(0), nextAliasingObject(0), nextIdObject(0) { + // initialize the members in the meta object + extObject.d.superdata = 0; + extObject.d.stringdata = 0; + extObject.d.data = 0; + extObject.d.extradata = 0; } QQmlScript::Object::~Object() diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 5412315d31..985d291a8e 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -204,13 +204,19 @@ inline bool fastHasBinding(QObject *o, int index) { QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(o)->declarativeData); + index &= 0xFFFFFF; // To handle value types + return ddata && (ddata->bindingBitsSize > index) && (ddata->bindingBits[index / 32] & (1 << (index % 32))); } static void removeBindingOnProperty(QObject *o, int index) { - QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, index, -1, 0); + int coreIndex = index & 0xFFFFFF; + int valueTypeIndex = index & 0xFF000000; + if (!valueTypeIndex) valueTypeIndex = -1; + + QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0); if (binding) binding->destroy(); } @@ -724,15 +730,10 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, QMetaMethod signal = target->metaObject()->method(instr.signalIndex); - QQmlAbstractBoundSignal *bs = 0; - if (signal.parameterTypes().count()) - bs = new QQmlBoundSignal(target, signal, target); - else - bs = new QQmlBoundSignalNoParams(target, signal, target); - QQmlExpression *expr = - new QQmlExpression(CTXT, context, DATAS.at(instr.value), true, COMP->name, instr.line, instr.column, *new QQmlExpressionPrivate); + QQmlBoundSignal *bs = new QQmlBoundSignal(target, signal, target); + QQmlBoundSignalExpression *expr = + new QQmlBoundSignalExpression(CTXT, context, DATAS.at(instr.value), true, COMP->name, instr.line, instr.column); bs->setExpression(expr); - bs->addToObject(); QML_END_INSTR(StoreSignal) QML_BEGIN_INSTR(StoreImportedScript) @@ -788,35 +789,23 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, bind->m_mePtr = &bindValues.top(); bind->setTarget(target, instr.property, CTXT); - typedef QQmlPropertyPrivate QDPP; - Q_ASSERT(bind->propertyIndex() == QDPP::bindingIndex(instr.property)); - Q_ASSERT(bind->object() == target); - - bind->addToObject(); - QML_END_INSTR(StoreBinding) - - QML_BEGIN_INSTR(StoreBindingOnAlias) - QObject *target = - objects.at(objects.count() - 1 - instr.owner); - QObject *context = - objects.at(objects.count() - 1 - instr.context); + if (instr.isAlias) { + QQmlAbstractBinding *old = + QQmlPropertyPrivate::setBindingNoEnable(target, + instr.property.coreIndex, + instr.property.getValueTypeCoreIndex(), + bind); + if (old) { old->destroy(); } + } else { + typedef QQmlPropertyPrivate QDPP; + Q_ASSERT(bind->propertyIndex() == QDPP::bindingIndex(instr.property)); + Q_ASSERT(bind->object() == target); - if (instr.isRoot && BINDINGSKIPLIST.testBit(instr.property.coreIndex)) - QML_NEXT_INSTR(StoreBindingOnAlias); + CLEAN_PROPERTY(target, QDPP::bindingIndex(instr.property)); - QQmlBinding *bind = new QQmlBinding(PRIMITIVES.at(instr.value), true, - context, CTXT, COMP->name, instr.line, - instr.column); - bindValues.push(bind); - bind->m_mePtr = &bindValues.top(); - bind->setTarget(target, instr.property, CTXT); - - QQmlAbstractBinding *old = - QQmlPropertyPrivate::setBindingNoEnable(target, instr.property.coreIndex, - instr.property.getValueTypeCoreIndex(), - bind); - if (old) { old->destroy(); } - QML_END_INSTR(StoreBindingOnAlias) + bind->addToObject(); + } + QML_END_INSTR(StoreBinding) QML_BEGIN_INSTR(StoreV4Binding) QObject *target = @@ -837,6 +826,8 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, Q_ASSERT(binding->propertyIndex() == (property & 0xFF00FFFF)); Q_ASSERT(binding->object() == target); + CLEAN_PROPERTY(target, property & 0xFF00FFFF); + binding->addToObject(); QML_END_INSTR(StoreV4Binding) @@ -855,11 +846,22 @@ QObject *QQmlVME::run(QList<QQmlError> *errors, bindValues.push(binding); binding->m_mePtr = &bindValues.top(); - typedef QQmlPropertyPrivate QDPP; - Q_ASSERT(binding->propertyIndex() == QDPP::bindingIndex(instr.property)); - Q_ASSERT(binding->object() == target); + if (instr.isAlias) { + QQmlAbstractBinding *old = + QQmlPropertyPrivate::setBindingNoEnable(target, + instr.property.coreIndex, + instr.property.getValueTypeCoreIndex(), + binding); + if (old) { old->destroy(); } + } else { + typedef QQmlPropertyPrivate QDPP; + Q_ASSERT(binding->propertyIndex() == QDPP::bindingIndex(instr.property)); + Q_ASSERT(binding->object() == target); + + CLEAN_PROPERTY(target, QDPP::bindingIndex(instr.property)); - binding->addToObject(); + binding->addToObject(); + } } QML_END_INSTR(StoreV8Binding) @@ -1242,9 +1244,9 @@ QQmlContextData *QQmlVME::complete(const Interrupt &interrupt) while (!bindValues.isEmpty()) { QQmlAbstractBinding *b = bindValues.pop(); - if(b) { + if (b) { b->m_mePtr = 0; - b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | + b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | QQmlPropertyPrivate::DontRemoveBinding); } diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp index 0cd6ffd082..4c92598c32 100644 --- a/src/qml/qml/v4/qv4bindings.cpp +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -52,6 +52,7 @@ #include <private/qqmlmetatype_p.h> #include <private/qqmltrace_p.h> #include <private/qqmlstringconverters_p.h> +#include <private/qqmlproperty_p.h> #include <QtQml/qqmlinfo.h> #include <QtCore/qnumeric.h> @@ -66,46 +67,53 @@ namespace { struct Register { typedef QQmlRegisterType Type; - void setUndefined() { dataType = UndefinedType; } - void setNull() { dataType = NullType; } - void setNaN() { setqreal(qSNaN()); } - bool isUndefined() const { return dataType == UndefinedType; } + inline void setUndefined() { dataType = UndefinedType; } + inline void setNull() { dataType = NullType; } + inline void setNaN() { setnumber(qSNaN()); } + inline bool isUndefined() const { return dataType == UndefinedType; } - void setQObject(QObject *o) { qobjectValue = o; dataType = QObjectStarType; } - QObject *getQObject() const { return qobjectValue; } + inline void setQObject(QObject *o) { qobjectValue = o; dataType = QObjectStarType; } + inline QObject *getQObject() const { return qobjectValue; } - void setqreal(qreal v) { qrealValue = v; dataType = QRealType; } - qreal getqreal() const { return qrealValue; } - qreal &getqrealref() { return qrealValue; } + inline void setnumber(double v) { numberValue = v; dataType = NumberType; } + inline double getnumber() const { return numberValue; } + inline double &getnumberref() { return numberValue; } - void setint(int v) { intValue = v; dataType = IntType; } - int getint() const { return intValue; } - int &getintref() { return intValue; } + inline void setfloat(float v) { floatValue = v; dataType = FloatType; } + inline float getfloat() const { return floatValue; } + inline float &getfloatref() { return floatValue; } - void setbool(bool v) { boolValue = v; dataType = BoolType; } - bool getbool() const { return boolValue; } - bool &getboolref() { return boolValue; } + inline void setint(int v) { intValue = v; dataType = IntType; } + inline int getint() const { return intValue; } + inline int &getintref() { return intValue; } - QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } - QString *getstringptr() { return (QString *)typeDataPtr(); } - QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } - const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } - const QString *getstringptr() const { return (QString *)typeDataPtr(); } - const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } + inline void setbool(bool v) { boolValue = v; dataType = BoolType; } + inline bool getbool() const { return boolValue; } + inline bool &getboolref() { return boolValue; } + + inline QVariant *getvariantptr() { return (QVariant *)typeDataPtr(); } + inline QString *getstringptr() { return (QString *)typeDataPtr(); } + inline QUrl *geturlptr() { return (QUrl *)typeDataPtr(); } + inline QColor *getcolorptr() { return (QColor *)typeDataPtr(); } + inline const QVariant *getvariantptr() const { return (QVariant *)typeDataPtr(); } + inline const QString *getstringptr() const { return (QString *)typeDataPtr(); } + inline const QUrl *geturlptr() const { return (QUrl *)typeDataPtr(); } + inline const QColor *getcolorptr() const { return (QColor *)typeDataPtr(); } size_t dataSize() { return sizeof(data); } - void *typeDataPtr() { return (void *)&data; } - void *typeMemory() { return (void *)data; } - const void *typeDataPtr() const { return (void *)&data; } - const void *typeMemory() const { return (void *)data; } + inline void *typeDataPtr() { return (void *)&data; } + inline void *typeMemory() { return (void *)data; } + inline const void *typeDataPtr() const { return (void *)&data; } + inline const void *typeMemory() const { return (void *)data; } - Type gettype() const { return dataType; } - void settype(Type t) { dataType = t; } + inline Type gettype() const { return dataType; } + inline void settype(Type t) { dataType = t; } Type dataType; // Type of data union { QObject *qobjectValue; - qreal qrealValue; + double numberValue; + float floatValue; int intValue; bool boolValue; void *data[sizeof(QVariant)]; @@ -387,85 +395,6 @@ void QV4Bindings::subscribe(QObject *o, int notifyIndex, int subIndex) sub->disconnect(); } -// Conversion functions - these MUST match the QtScript expression path -inline static qreal toReal(Register *reg, int type, bool *ok = 0) -{ - if (ok) *ok = true; - - if (type == QMetaType::QReal) { - return reg->getqreal(); - } else if (type == qMetaTypeId<QVariant>()) { - return reg->getvariantptr()->toReal(); - } else { - if (ok) *ok = false; - return 0; - } -} - -inline static QString toString(Register *reg, int type, bool *ok = 0) -{ - if (ok) *ok = true; - - if (type == QMetaType::QReal) { - return QString::number(reg->getqreal()); - } else if (type == QMetaType::Int) { - return QString::number(reg->getint()); - } else if (type == qMetaTypeId<QVariant>()) { - return reg->getvariantptr()->toString(); - } else if (type == QMetaType::QString) { - return *reg->getstringptr(); - } else { - if (ok) *ok = false; - return QString(); - } -} - -inline static bool toBool(Register *reg, int type, bool *ok = 0) -{ - if (ok) *ok = true; - - if (type == QMetaType::Bool) { - return reg->getbool(); - } else if (type == qMetaTypeId<QVariant>()) { - return reg->getvariantptr()->toBool(); - } else { - if (ok) *ok = false; - return false; - } -} - -inline static QUrl toUrl(Register *reg, int type, QQmlContextData *context, bool *ok = 0) -{ - if (ok) *ok = true; - - QUrl base; - if (type == qMetaTypeId<QVariant>()) { - QVariant *var = reg->getvariantptr(); - int vt = var->type(); - if (vt == QVariant::Url) { - base = var->toUrl(); - } else if (vt == QVariant::ByteArray) { - // Preserve any valid percent-encoded octets supplied by the source - base.setEncodedUrl(var->toByteArray(), QUrl::TolerantMode); - } else if (vt == QVariant::String) { - base.setEncodedUrl(var->toString().toUtf8(), QUrl::TolerantMode); - } else { - if (ok) *ok = false; - return QUrl(); - } - } else if (type == QMetaType::QString) { - base.setEncodedUrl(reg->getstringptr()->toUtf8(), QUrl::TolerantMode); - } else { - if (ok) *ok = false; - return QUrl(); - } - - if (!base.isEmpty() && base.isRelative()) - return context->url.resolved(base); - else - return base; -} - static bool testCompareVariants(const QVariant &qtscriptRaw, const QVariant &v4) { QVariant qtscript = qtscriptRaw; @@ -529,10 +458,29 @@ static void testBindingResult(const QString &binding, int line, int column, if (expression.hasError()) { iserror = true; qtscriptResult = "exception"; - } else { - qtscriptResult = testResultToString(value, isUndefined); + } else if (value.userType() != resultType) { + // Override the QMetaType conversions to make them more JS friendly. + if (value.userType() == QMetaType::Double && (resultType == QMetaType::QString || + resultType == QMetaType::QUrl)) { + // number to string-like conversion. + value = QVariant::fromValue<QString>(QString::number(value.toDouble(), 'g', 16)); + } else if (value.userType() == QMetaType::QUrl && resultType == QMetaType::Bool) { + // url to bool conversion + value = QVariant::fromValue<bool>(!value.toUrl().isEmpty()); + } + + if (!value.isNull() && !value.convert(resultType)) { + iserror = true; + qtscriptResult = "exception"; + } else if (resultType == QMetaType::QUrl) { + // a V8 value was converted to QUrl. + value = QVariant::fromValue<QUrl>(context->resolvedUrl(value.toUrl())); + } } + if (! iserror) + qtscriptResult = testResultToString(value, isUndefined); + if (isUndefined && result.isUndefined()) { return; } else if(isUndefined != result.isUndefined()) { @@ -557,8 +505,8 @@ static void testBindingResult(const QString &binding, int line, int column, case QMetaType::Int: v4value = result.getint(); break; - case QMetaType::QReal: - v4value = result.getqreal(); + case QMetaType::Double: + v4value = result.getnumber(); break; default: if (resultType == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { @@ -621,15 +569,15 @@ static void throwException(int id, QQmlDelayedError *error, QQmlEnginePrivate::warning(context->engine, error->error); } -const qreal QV4Bindings::D32 = 4294967296.0; +const double QV4Bindings::D32 = 4294967296.0; -qint32 QV4Bindings::toInt32(qreal n) +qint32 QV4Bindings::toInt32(double n) { if (qIsNaN(n) || qIsInf(n) || (n == 0)) return 0; double sign = (n < 0) ? -1.0 : 1.0; - qreal abs_n = fabs(n); + double abs_n = fabs(n); n = ::fmod(sign * ::floor(abs_n), D32); const double D31 = D32 / 2.0; @@ -643,13 +591,13 @@ qint32 QV4Bindings::toInt32(qreal n) return qint32 (n); } -inline quint32 QV4Bindings::toUint32(qreal n) +inline quint32 QV4Bindings::toUint32(double n) { if (qIsNaN(n) || qIsInf(n) || (n == 0)) return 0; double sign = (n < 0) ? -1.0 : 1.0; - qreal abs_n = fabs(n); + double abs_n = fabs(n); n = ::fmod(sign * ::floor(abs_n), D32); @@ -665,6 +613,11 @@ inline quint32 QV4Bindings::toUint32(qreal n) goto exceptionExit; \ } +#define THROW_VALUE_EXCEPTION_STR(id, str) { \ + throwException((id), error, program, context, (str)); \ + goto exceptionExit; \ +} + #define THROW_EXCEPTION(id) THROW_EXCEPTION_STR(id, QString()) #define MARK_REGISTER(reg) cleanupRegisterMask |= (1 << (reg)) @@ -791,13 +744,21 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, sub->bindings = this; sub->method = subIdx; } - reg.init((Register::Type)instr->fetchAndSubscribe.valueType); + + const Register::Type valueType = (Register::Type)instr->fetchAndSubscribe.valueType; + reg.init(valueType); if (instr->fetchAndSubscribe.valueType >= FirstCleanupType) MARK_REGISTER(instr->fetchAndSubscribe.reg); QQmlAccessors *accessors = instr->fetchAndSubscribe.property.accessors; accessors->read(object, instr->fetchAndSubscribe.property.accessorData, reg.typeDataPtr()); + if (valueType == FloatType) { + // promote floats + const double v = reg.getfloat(); + reg.setnumber(v); + } + if (accessors->notifier) { QQmlNotifier *notifier = 0; accessors->notifier(object, instr->fetchAndSubscribe.property.accessorData, ¬ifier); @@ -868,11 +829,11 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(UnaryNot, unaryop) - QML_V4_BEGIN_INSTR(UnaryMinusReal, unaryop) + QML_V4_BEGIN_INSTR(UnaryMinusNumber, unaryop) { - registers[instr->unaryop.output].setqreal(-registers[instr->unaryop.src].getqreal()); + registers[instr->unaryop.output].setnumber(-registers[instr->unaryop.src].getnumber()); } - QML_V4_END_INSTR(UnaryMinusReal, unaryop) + QML_V4_END_INSTR(UnaryMinusNumber, unaryop) QML_V4_BEGIN_INSTR(UnaryMinusInt, unaryop) { @@ -880,11 +841,11 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(UnaryMinusInt, unaryop) - QML_V4_BEGIN_INSTR(UnaryPlusReal, unaryop) + QML_V4_BEGIN_INSTR(UnaryPlusNumber, unaryop) { - registers[instr->unaryop.output].setqreal(+registers[instr->unaryop.src].getqreal()); + registers[instr->unaryop.output].setnumber(+registers[instr->unaryop.src].getnumber()); } - QML_V4_END_INSTR(UnaryPlusReal, unaryop) + QML_V4_END_INSTR(UnaryPlusNumber, unaryop) QML_V4_BEGIN_INSTR(UnaryPlusInt, unaryop) { @@ -901,14 +862,14 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(ConvertBoolToInt, unaryop) - QML_V4_BEGIN_INSTR(ConvertBoolToReal, unaryop) + QML_V4_BEGIN_INSTR(ConvertBoolToNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setqreal(src.getbool()); + else output.setnumber(src.getbool()); } - QML_V4_END_INSTR(ConvertBoolToReal, unaryop) + QML_V4_END_INSTR(ConvertBoolToNumber, unaryop) QML_V4_BEGIN_INSTR(ConvertBoolToString, unaryop) { @@ -932,14 +893,14 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(ConvertIntToBool, unaryop) - QML_V4_BEGIN_INSTR(ConvertIntToReal, unaryop) + QML_V4_BEGIN_INSTR(ConvertIntToNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setqreal(qreal(src.getint())); + else output.setnumber(double(src.getint())); } - QML_V4_END_INSTR(ConvertIntToReal, unaryop) + QML_V4_END_INSTR(ConvertIntToNumber, unaryop) QML_V4_BEGIN_INSTR(ConvertIntToString, unaryop) { @@ -954,25 +915,25 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(ConvertIntToString, unaryop) - QML_V4_BEGIN_INSTR(ConvertRealToBool, unaryop) + QML_V4_BEGIN_INSTR(ConvertNumberToBool, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setbool(src.getqreal() != 0); + else output.setbool(src.getnumber() != 0); } - QML_V4_END_INSTR(ConvertRealToBool, unaryop) + QML_V4_END_INSTR(ConvertNumberToBool, unaryop) - QML_V4_BEGIN_INSTR(ConvertRealToInt, unaryop) + QML_V4_BEGIN_INSTR(ConvertNumberToInt, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setint(toInt32(src.getqreal())); + else output.setint(toInt32(src.getnumber())); } - QML_V4_END_INSTR(ConvertRealToInt, unaryop) + QML_V4_END_INSTR(ConvertNumberToInt, unaryop) - QML_V4_BEGIN_INSTR(ConvertRealToString, unaryop) + QML_V4_BEGIN_INSTR(ConvertNumberToString, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; @@ -980,11 +941,11 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, if (src.isUndefined()) { output.setUndefined(); } else { - new (output.getstringptr()) QString(QString::number(src.getqreal())); + new (output.getstringptr()) QString(QString::number(src.getnumber(), 'g', 16)); STRING_REGISTER(instr->unaryop.output); } } - QML_V4_END_INSTR(ConvertRealToString, unaryop) + QML_V4_END_INSTR(ConvertNumberToString, unaryop) QML_V4_BEGIN_INSTR(ConvertStringToBool, unaryop) { @@ -1026,7 +987,7 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(ConvertStringToInt, unaryop) - QML_V4_BEGIN_INSTR(ConvertStringToReal, unaryop) + QML_V4_BEGIN_INSTR(ConvertStringToNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; @@ -1041,10 +1002,10 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, output.cleanupString(); MARK_CLEAN_REGISTER(instr->unaryop.output); } - output.setqreal(tmp.toNumber()); + output.setnumber(tmp.toNumber()); } } - QML_V4_END_INSTR(ConvertStringToReal, unaryop) + QML_V4_END_INSTR(ConvertStringToNumber, unaryop) QML_V4_BEGIN_INSTR(ConvertStringToUrl, unaryop) { @@ -1190,75 +1151,75 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(ResolveUrl, unaryop) - QML_V4_BEGIN_INSTR(MathSinReal, unaryop) + QML_V4_BEGIN_INSTR(MathSinNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setqreal(qSin(src.getqreal())); + else output.setnumber(qSin(src.getnumber())); } - QML_V4_END_INSTR(MathSinReal, unaryop) + QML_V4_END_INSTR(MathSinNumber, unaryop) - QML_V4_BEGIN_INSTR(MathCosReal, unaryop) + QML_V4_BEGIN_INSTR(MathCosNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setqreal(qCos(src.getqreal())); + else output.setnumber(qCos(src.getnumber())); } - QML_V4_END_INSTR(MathCosReal, unaryop) + QML_V4_END_INSTR(MathCosNumber, unaryop) - QML_V4_BEGIN_INSTR(MathAbsReal, unaryop) + QML_V4_BEGIN_INSTR(MathAbsNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setqreal(qAbs(src.getqreal())); + else output.setnumber(qAbs(src.getnumber())); } - QML_V4_END_INSTR(MathAbsReal, unaryop) + QML_V4_END_INSTR(MathAbsNumber, unaryop) - QML_V4_BEGIN_INSTR(MathRoundReal, unaryop) + QML_V4_BEGIN_INSTR(MathRoundNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setint(qRound(src.getqreal())); + else output.setint(qRound(src.getnumber())); } - QML_V4_END_INSTR(MathRoundReal, unaryop) + QML_V4_END_INSTR(MathRoundNumber, unaryop) - QML_V4_BEGIN_INSTR(MathFloorReal, unaryop) + QML_V4_BEGIN_INSTR(MathFloorNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setint(qFloor(src.getqreal())); + else output.setint(qFloor(src.getnumber())); } - QML_V4_END_INSTR(MathFloorReal, unaryop) + QML_V4_END_INSTR(MathFloorNumber, unaryop) - QML_V4_BEGIN_INSTR(MathCeilReal, unaryop) + QML_V4_BEGIN_INSTR(MathCeilNumber, unaryop) { const Register &src = registers[instr->unaryop.src]; Register &output = registers[instr->unaryop.output]; if (src.isUndefined()) output.setUndefined(); - else output.setint(qCeil(src.getqreal())); + else output.setint(qCeil(src.getnumber())); } - QML_V4_END_INSTR(MathCeilReal, unaryop) + QML_V4_END_INSTR(MathCeilNumber, unaryop) - QML_V4_BEGIN_INSTR(MathPIReal, unaryop) + QML_V4_BEGIN_INSTR(MathPINumber, unaryop) { - static const qreal qmlPI = 2.0 * qAsin(1.0); + static const double qmlPI = 2.0 * qAsin(1.0); Register &output = registers[instr->unaryop.output]; - output.setqreal(qmlPI); + output.setnumber(qmlPI); } - QML_V4_END_INSTR(MathPIReal, unaryop) + QML_V4_END_INSTR(MathPINumber, unaryop) QML_V4_BEGIN_INSTR(LoadNull, null_value) registers[instr->null_value.reg].setNull(); QML_V4_END_INSTR(LoadNull, null_value) - QML_V4_BEGIN_INSTR(LoadReal, real_value) - registers[instr->real_value.reg].setqreal(instr->real_value.value); - QML_V4_END_INSTR(LoadReal, real_value) + QML_V4_BEGIN_INSTR(LoadNumber, number_value) + registers[instr->number_value.reg].setnumber(instr->number_value.value); + QML_V4_END_INSTR(LoadNumber, number_value) QML_V4_BEGIN_INSTR(LoadInt, int_value) registers[instr->int_value.reg].setint(instr->int_value.value); @@ -1305,12 +1266,12 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(BitXorInt, binaryop) - QML_V4_BEGIN_INSTR(AddReal, binaryop) + QML_V4_BEGIN_INSTR(AddNumber, binaryop) { - registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() + - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() + + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(AddReal, binaryop) + QML_V4_END_INSTR(AddNumber, binaryop) QML_V4_BEGIN_INSTR(AddString, binaryop) { @@ -1324,36 +1285,33 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(AddString, binaryop) - QML_V4_BEGIN_INSTR(SubReal, binaryop) + QML_V4_BEGIN_INSTR(SubNumber, binaryop) { - registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() - - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() - + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(SubReal, binaryop) + QML_V4_END_INSTR(SubNumber, binaryop) - QML_V4_BEGIN_INSTR(MulReal, binaryop) + QML_V4_BEGIN_INSTR(MulNumber, binaryop) { - registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() * - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() * + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(MulReal, binaryop) + QML_V4_END_INSTR(MulNumber, binaryop) - QML_V4_BEGIN_INSTR(DivReal, binaryop) + QML_V4_BEGIN_INSTR(DivNumber, binaryop) { - registers[instr->binaryop.output].setqreal(registers[instr->binaryop.left].getqreal() / - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setnumber(registers[instr->binaryop.left].getnumber() / + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(DivReal, binaryop) + QML_V4_END_INSTR(DivNumber, binaryop) - QML_V4_BEGIN_INSTR(ModReal, binaryop) + QML_V4_BEGIN_INSTR(ModNumber, binaryop) { Register &target = registers[instr->binaryop.output]; const Register &left = registers[instr->binaryop.left]; const Register &right = registers[instr->binaryop.right]; - if (QMetaType::QReal == QMetaType::Float) - target.setqreal(::fmodf(left.getqreal(), right.getqreal())); - else - target.setqreal(::fmod(left.getqreal(), right.getqreal())); + target.setnumber(::fmod(left.getnumber(), right.getnumber())); } QML_V4_END_INSTR(ModInt, binaryop) @@ -1378,61 +1336,61 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(URShiftInt, binaryop) - QML_V4_BEGIN_INSTR(GtReal, binaryop) + QML_V4_BEGIN_INSTR(GtNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() > - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() > + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(GtReal, binaryop) + QML_V4_END_INSTR(GtNumber, binaryop) - QML_V4_BEGIN_INSTR(LtReal, binaryop) + QML_V4_BEGIN_INSTR(LtNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() < - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() < + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(LtReal, binaryop) + QML_V4_END_INSTR(LtNumber, binaryop) - QML_V4_BEGIN_INSTR(GeReal, binaryop) + QML_V4_BEGIN_INSTR(GeNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() >= - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() >= + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(GeReal, binaryop) + QML_V4_END_INSTR(GeNumber, binaryop) - QML_V4_BEGIN_INSTR(LeReal, binaryop) + QML_V4_BEGIN_INSTR(LeNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() <= - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() <= + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(LeReal, binaryop) + QML_V4_END_INSTR(LeNumber, binaryop) - QML_V4_BEGIN_INSTR(EqualReal, binaryop) + QML_V4_BEGIN_INSTR(EqualNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() == + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(EqualReal, binaryop) + QML_V4_END_INSTR(EqualNumber, binaryop) - QML_V4_BEGIN_INSTR(NotEqualReal, binaryop) + QML_V4_BEGIN_INSTR(NotEqualNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() != + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(NotEqualReal, binaryop) + QML_V4_END_INSTR(NotEqualNumber, binaryop) - QML_V4_BEGIN_INSTR(StrictEqualReal, binaryop) + QML_V4_BEGIN_INSTR(StrictEqualNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() == - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() == + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(StrictEqualReal, binaryop) + QML_V4_END_INSTR(StrictEqualNumber, binaryop) - QML_V4_BEGIN_INSTR(StrictNotEqualReal, binaryop) + QML_V4_BEGIN_INSTR(StrictNotEqualNumber, binaryop) { - registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getqreal() != - registers[instr->binaryop.right].getqreal()); + registers[instr->binaryop.output].setbool(registers[instr->binaryop.left].getnumber() != + registers[instr->binaryop.right].getnumber()); } - QML_V4_END_INSTR(StrictNotEqualReal, binaryop) + QML_V4_END_INSTR(StrictNotEqualNumber, binaryop) QML_V4_BEGIN_INSTR(GtString, binaryop) { @@ -1578,25 +1536,25 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QML_V4_END_INSTR(StrictNotEqualObject, binaryop) - QML_V4_BEGIN_INSTR(MathMaxReal, binaryop) + QML_V4_BEGIN_INSTR(MathMaxNumber, binaryop) { const Register &left = registers[instr->binaryop.left]; const Register &right = registers[instr->binaryop.right]; Register &output = registers[instr->binaryop.output]; if (left.isUndefined() || right.isUndefined()) output.setUndefined(); - else output.setqreal(qMax(left.getqreal(), right.getqreal())); + else output.setnumber(qMax(left.getnumber(), right.getnumber())); } - QML_V4_END_INSTR(MathMaxReal, binaryop) + QML_V4_END_INSTR(MathMaxNumber, binaryop) - QML_V4_BEGIN_INSTR(MathMinReal, binaryop) + QML_V4_BEGIN_INSTR(MathMinNumber, binaryop) { const Register &left = registers[instr->binaryop.left]; const Register &right = registers[instr->binaryop.right]; Register &output = registers[instr->binaryop.output]; if (left.isUndefined() || right.isUndefined()) output.setUndefined(); - else output.setqreal(qMin(left.getqreal(), right.getqreal())); + else output.setnumber(qMin(left.getnumber(), right.getnumber())); } - QML_V4_END_INSTR(MathMinReal, binaryop) + QML_V4_END_INSTR(MathMinNumber, binaryop) QML_V4_BEGIN_INSTR(NewString, construct) { @@ -1625,11 +1583,17 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, if (!object) { THROW_EXCEPTION(instr->fetch.exceptionId); } else { - reg.init((Register::Type)instr->fetch.valueType); + const Register::Type valueType = (Register::Type)instr->fetch.valueType; + reg.init(valueType); if (instr->fetch.valueType >= FirstCleanupType) MARK_REGISTER(instr->fetch.reg); void *argv[] = { reg.typeDataPtr(), 0 }; QMetaObject::metacall(object, QMetaObject::ReadProperty, instr->fetch.index, argv); + if (valueType == FloatType) { + // promote floats + const double v = reg.getfloat(); + reg.setnumber(v); + } } } QML_V4_END_INSTR(Fetch, fetch) @@ -1649,6 +1613,30 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, if (data.isUndefined()) THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign undefined value")); + if (data.gettype() == QObjectStarType) { + if (QObject *dataObject = data.getQObject()) { + const QMetaObject *dataMo = dataObject->metaObject(); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + QMetaProperty receiver = output->metaObject()->property(instr->store.index); + const QMetaObject *receiverMo = QQmlPropertyPrivate::rawMetaObjectForType(ep, receiver.userType()); + + // Verify that these types are compatible + if (!QQmlPropertyPrivate::canConvert(dataMo, receiverMo)) { + THROW_EXCEPTION_STR(instr->store.exceptionId, QLatin1String("Unable to assign ") + + QLatin1String(dataMo->className()) + + QLatin1String(" to ") + + QLatin1String(receiverMo->className())); + } + } + } + + if (instr->store.valueType == FloatType) { + // cast numbers to floats + const float v = (float) data.getnumber(); + data.setfloat(v); + } + int status = -1; void *argv[] = { data.typeDataPtr(), 0, &status, &storeFlags }; QMetaObject::metacall(output, QMetaObject::WriteProperty, @@ -1687,22 +1675,14 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, executedBlocks |= instr->blockop.block; QML_V4_END_INSTR(Block, blockop) - // XXX not applicable in v8 - QML_V4_BEGIN_INSTR(InitString, initstring) -// if (!identifiers[instr->initstring.offset].identifier) { -// quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); -// QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); - -// QString str = QString::fromRawData(strdata, len); - -// // identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); -// } - QML_V4_END_INSTR(InitString, initstring) - QML_V4_BEGIN_INSTR(CleanupRegister, cleanup) registers[instr->cleanup.reg].cleanup(); QML_V4_END_INSTR(CleanupRegister, cleanup) + QML_V4_BEGIN_INSTR(Throw, throwop) + THROW_VALUE_EXCEPTION_STR(instr->throwop.exceptionId, *registers[instr->throwop.message].getstringptr()); + QML_V4_END_INSTR(Throw, throwop) + #ifdef QML_THREADED_INTERPRETER // nothing to do #else diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h index 1824fa4ee9..3a7d175d7b 100644 --- a/src/qml/qml/v4/qv4bindings_p.h +++ b/src/qml/qml/v4/qv4bindings_p.h @@ -141,9 +141,9 @@ private: inline void subscribeId(QQmlContextData *p, int idIndex, int subIndex); inline void subscribe(QObject *o, int notifyIndex, int subIndex); - inline static qint32 toInt32(qreal n); - static const qreal D32; - static quint32 toUint32(qreal n); + inline static qint32 toInt32(double n); + static const double D32; + static quint32 toUint32(double n); }; diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp index c9495e8987..045a14fbb5 100644 --- a/src/qml/qml/v4/qv4compiler.cpp +++ b/src/qml/qml/v4/qv4compiler.cpp @@ -62,7 +62,9 @@ static bool qmlEnableV4 = true; using namespace QQmlJS; QV4CompilerPrivate::QV4CompilerPrivate() - : _function(0) , _block(0) , _discarded(false), registerCount(0) + : subscriptionOffset(0) + , _function(0) , _block(0) , _discarded(false), registerCount(0) + , bindingLine(0), bindingColumn(0) { } @@ -73,6 +75,8 @@ void QV4CompilerPrivate::trace(int line, int column) { bytecode.clear(); + this->bindingLine = line; + this->bindingColumn = column; this->currentReg = _function->tempCount; this->registerCount = qMax(this->registerCount, this->currentReg); @@ -226,8 +230,9 @@ void QV4CompilerPrivate::visitConst(IR::Const *e) gen(i); } break; - case IR::RealType: { - Instr::LoadReal i; + case IR::FloatType: + case IR::NumberType: { + Instr::LoadNumber i; i.reg = currentReg; i.value = e->value; gen(i); @@ -316,7 +321,7 @@ void QV4CompilerPrivate::visitName(IR::Name *e) Instr::LoadAttached attached; attached.output = currentReg; attached.reg = currentReg; - attached.exceptionId = exceptionId(e->line, e->column); + attached.exceptionId = exceptionId(bindingLine, bindingColumn); if (e->declarativeType->attachedPropertiesId() == -1) discard(); attached.id = e->declarativeType->attachedPropertiesId(); @@ -351,8 +356,11 @@ void QV4CompilerPrivate::visitName(IR::Name *e) QQmlRegisterType regType; switch (propTy) { - case QMetaType::QReal: - regType = QRealType; + case QMetaType::Float: + regType = FloatType; + break; + case QMetaType::Double: + regType = NumberType; break; case QMetaType::Bool: regType = BoolType; @@ -373,7 +381,7 @@ void QV4CompilerPrivate::visitName(IR::Name *e) default: if (propTy == QQmlMetaType::QQuickAnchorLineMetaTypeId()) { regType = PODValueType; - } else if (QQmlMetaType::isQObject(propTy)) { + } else if (engine->metaObjectForType(propTy)) { regType = QObjectStarType; } else { if (qmlVerboseCompiler()) @@ -458,14 +466,14 @@ void QV4CompilerPrivate::visitUnop(IR::Unop *e) } break; case IR::OpUMinus: - if (e->expr->type == IR::RealType) { - Instr::UnaryMinusReal i; + if (IR::isRealType(e->expr->type)) { + Instr::UnaryMinusNumber i; i.output = currentReg; i.src = src; gen(i); } else if (e->expr->type == IR::IntType) { - convertToReal(e->expr, currentReg); - Instr::UnaryMinusReal i; + convertToNumber(e->expr, currentReg); + Instr::UnaryMinusNumber i; i.output = currentReg; i.src = src; gen(i); @@ -475,14 +483,14 @@ void QV4CompilerPrivate::visitUnop(IR::Unop *e) break; case IR::OpUPlus: - if (e->expr->type == IR::RealType) { - Instr::UnaryPlusReal i; + if (IR::isRealType(e->expr->type)) { + Instr::UnaryPlusNumber i; i.output = currentReg; i.src = src; gen(i); } else if (e->expr->type == IR::IntType) { - convertToReal(e->expr, currentReg); - Instr::UnaryPlusReal i; + convertToNumber(e->expr, currentReg); + Instr::UnaryPlusNumber i; i.output = currentReg; i.src = src; gen(i); @@ -522,25 +530,26 @@ void QV4CompilerPrivate::visitUnop(IR::Unop *e) } // switch } -void QV4CompilerPrivate::convertToReal(IR::Expr *expr, int reg) +void QV4CompilerPrivate::convertToNumber(IR::Expr *expr, int reg) { - if (expr->type == IR::RealType) + if (expr->type == IR::NumberType) return; switch (expr->type) { case IR::BoolType: { - Instr::ConvertBoolToReal i; + Instr::ConvertBoolToNumber i; i.output = i.src = reg; gen(i); } break; case IR::IntType: { - Instr::ConvertIntToReal i; + Instr::ConvertIntToNumber i; i.output = i.src = reg; gen(i); } break; - case IR::RealType: + case IR::FloatType: + case IR::NumberType: // nothing to do return; @@ -566,8 +575,9 @@ void QV4CompilerPrivate::convertToInt(IR::Expr *expr, int reg) // nothing to do return; - case IR::RealType: { - Instr::ConvertRealToInt i; + case IR::FloatType: + case IR::NumberType: { + Instr::ConvertNumberToInt i; i.output = i.src = reg; gen(i); } break; @@ -594,8 +604,9 @@ void QV4CompilerPrivate::convertToBool(IR::Expr *expr, int reg) gen(i); } break; - case IR::RealType: { - Instr::ConvertRealToBool i; + case IR::FloatType: + case IR::NumberType: { + Instr::ConvertNumberToBool i; i.output = i.src = reg; gen(i); } return; @@ -643,19 +654,19 @@ quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e) case IR::OpAdd: if (e->type == IR::StringType) return V4Instr::AddString; - return V4Instr::AddReal; + return V4Instr::AddNumber; case IR::OpSub: - return V4Instr::SubReal; + return V4Instr::SubNumber; case IR::OpMul: - return V4Instr::MulReal; + return V4Instr::MulNumber; case IR::OpDiv: - return V4Instr::DivReal; + return V4Instr::DivNumber; case IR::OpMod: - return V4Instr::ModReal; + return V4Instr::ModNumber; case IR::OpLShift: return V4Instr::LShiftInt; @@ -669,50 +680,50 @@ quint8 QV4CompilerPrivate::instructionOpcode(IR::Binop *e) case IR::OpGt: if (e->left->type == IR::StringType) return V4Instr::GtString; - return V4Instr::GtReal; + return V4Instr::GtNumber; case IR::OpLt: if (e->left->type == IR::StringType) return V4Instr::LtString; - return V4Instr::LtReal; + return V4Instr::LtNumber; case IR::OpGe: if (e->left->type == IR::StringType) return V4Instr::GeString; - return V4Instr::GeReal; + return V4Instr::GeNumber; case IR::OpLe: if (e->left->type == IR::StringType) return V4Instr::LeString; - return V4Instr::LeReal; + return V4Instr::LeNumber; case IR::OpEqual: if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) return V4Instr::EqualObject; if (e->left->type == IR::StringType) return V4Instr::EqualString; - return V4Instr::EqualReal; + return V4Instr::EqualNumber; case IR::OpNotEqual: if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) return V4Instr::NotEqualObject; if (e->left->type == IR::StringType) return V4Instr::NotEqualString; - return V4Instr::NotEqualReal; + return V4Instr::NotEqualNumber; case IR::OpStrictEqual: if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) return V4Instr::StrictEqualObject; if (e->left->type == IR::StringType) return V4Instr::StrictEqualString; - return V4Instr::StrictEqualReal; + return V4Instr::StrictEqualNumber; case IR::OpStrictNotEqual: if (e->left->type == IR::ObjectType || e->right->type == IR::ObjectType) return V4Instr::StrictNotEqualObject; if (e->left->type == IR::StringType) return V4Instr::StrictNotEqualString; - return V4Instr::StrictNotEqualReal; + return V4Instr::StrictNotEqualNumber; case IR::OpAnd: case IR::OpOr: @@ -733,12 +744,26 @@ void QV4CompilerPrivate::visitBinop(IR::Binop *e) int left = currentReg; int right = currentReg + 1; - traceExpression(e->left, left); - traceExpression(e->right, right); + if (e->left->asTemp() && e->type != IR::StringType) + left = e->left->asTemp()->index; + else + traceExpression(e->left, left); - // At this point it is possible that the type of the - // subexpressions is different. This can happen because - // we keep BINOP expressions in HIR. + if (IR::Temp *t = e->right->asTemp()) + right = t->index; + else + traceExpression(e->right, right); + + if (e->left->type != e->right->type) { + if (qmlVerboseCompiler()) + qWarning().nospace() << "invalid operands to binary operator " << IR::binaryOperator(e->op) + << "(`" << IR::binaryOperator(e->left->type) + << "' and `" + << IR::binaryOperator(e->right->type) + << "'"; + discard(); + return; + } switch (e->op) { case IR::OpInvalid: @@ -766,8 +791,8 @@ void QV4CompilerPrivate::visitBinop(IR::Binop *e) case IR::OpAdd: if (e->type != IR::StringType) { - convertToReal(e->left, left); - convertToReal(e->right, right); + convertToNumber(e->left, left); + convertToNumber(e->right, right); } break; @@ -775,8 +800,8 @@ void QV4CompilerPrivate::visitBinop(IR::Binop *e) case IR::OpMul: case IR::OpDiv: case IR::OpMod: - convertToReal(e->left, left); - convertToReal(e->right, right); + convertToNumber(e->left, left); + convertToNumber(e->right, right); break; case IR::OpGt: @@ -788,8 +813,8 @@ void QV4CompilerPrivate::visitBinop(IR::Binop *e) case IR::OpStrictEqual: case IR::OpStrictNotEqual: if (e->left->type >= IR::FirstNumberType) { - convertToReal(e->left, left); - convertToReal(e->right, right); + convertToNumber(e->left, left); + convertToNumber(e->right, right); } break; @@ -813,7 +838,7 @@ void QV4CompilerPrivate::visitCall(IR::Call *call) { if (IR::Name *name = call->base->asName()) { IR::Expr *arg = call->onlyArgument(); - if (arg != 0 && arg->type == IR::RealType) { + if (arg != 0 && IR::isRealType(arg->type)) { traceExpression(arg, currentReg); switch (name->builtin) { @@ -821,37 +846,37 @@ void QV4CompilerPrivate::visitCall(IR::Call *call) break; case IR::MathSinBultinFunction: { - Instr::MathSinReal i; + Instr::MathSinNumber i; i.output = i.src = currentReg; gen(i); } return; case IR::MathCosBultinFunction: { - Instr::MathCosReal i; + Instr::MathCosNumber i; i.output = i.src = currentReg; gen(i); } return; case IR::MathAbsBuiltinFunction: { - Instr::MathAbsReal i; + Instr::MathAbsNumber i; i.output = i.src = currentReg; gen(i); } return; case IR::MathRoundBultinFunction: { - Instr::MathRoundReal i; + Instr::MathRoundNumber i; i.output = i.src = currentReg; gen(i); } return; case IR::MathFloorBultinFunction: { - Instr::MathFloorReal i; + Instr::MathFloorNumber i; i.output = i.src = currentReg; gen(i); } return; case IR::MathCeilBuiltinFunction: { - Instr::MathCeilReal i; + Instr::MathCeilNumber i; i.output = i.src = currentReg; gen(i); } return; @@ -869,21 +894,21 @@ void QV4CompilerPrivate::visitCall(IR::Call *call) IR::Expr *arg1 = call->args->expr; IR::Expr *arg2 = call->args->next->expr; - if (arg1 != 0 && arg1->type == IR::RealType && - arg2 != 0 && arg2->type == IR::RealType) { + if (arg1 != 0 && IR::isRealType(arg1->type) && + arg2 != 0 && IR::isRealType(arg2->type)) { traceExpression(arg1, currentReg); traceExpression(arg2, currentReg + 1); if (name->builtin == IR::MathMaxBuiltinFunction) { - Instr::MathMaxReal i; + Instr::MathMaxNumber i; i.left = currentReg; i.right = currentReg + 1; i.output = currentReg; gen(i); return; } else if (name->builtin == IR::MathMinBuiltinFunction) { - Instr::MathMinReal i; + Instr::MathMinNumber i; i.left = currentReg; i.right = currentReg + 1; i.output = currentReg; @@ -917,7 +942,17 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) quint8 dest = target->index; - if (target->type != s->source->type) { + IR::Type targetTy = s->target->type; + IR::Type sourceTy = s->source->type; + + // promote the floats + if (sourceTy == IR::FloatType) + sourceTy = IR::NumberType; + + if (targetTy == IR::FloatType) + targetTy = IR::NumberType; + + if (sourceTy != targetTy) { quint8 src = dest; if (IR::Temp *t = s->source->asTemp()) @@ -926,8 +961,6 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) traceExpression(s->source, dest); V4Instr::Type opcode = V4Instr::Noop; - IR::Type targetTy = s->target->type; - IR::Type sourceTy = s->source->type; if (sourceTy == IR::UrlType) { switch (targetTy) { @@ -937,13 +970,22 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) // url-to-xxx conversions. break; default: { + if (s->isMoveForReturn) { + V4Instr instr; + instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn); + registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2") + .arg(QLatin1String(IR::typeName(sourceTy))) + .arg(QLatin1String(IR::typeName(targetTy))))); + instr.throwop.message = dest; + gen(V4Instr::Throw, instr); + return; + } // generate a UrlToString conversion and fix // the type of the source expression. V4Instr conv; - conv.unaryop.output = V4Instr::ConvertUrlToString; + conv.unaryop.output = src; conv.unaryop.src = src; - gen(opcode, conv); - + gen(V4Instr::ConvertUrlToString, conv); sourceTy = IR::StringType; break; } @@ -953,7 +995,7 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) if (targetTy == IR::BoolType) { switch (sourceTy) { case IR::IntType: opcode = V4Instr::ConvertIntToBool; break; - case IR::RealType: opcode = V4Instr::ConvertRealToBool; break; + case IR::NumberType: opcode = V4Instr::ConvertNumberToBool; break; case IR::StringType: opcode = V4Instr::ConvertStringToBool; break; case IR::UrlType: opcode = V4Instr::ConvertUrlToBool; break; case IR::ColorType: opcode = V4Instr::ConvertColorToBool; break; @@ -963,33 +1005,44 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) } else if (targetTy == IR::IntType) { switch (sourceTy) { case IR::BoolType: opcode = V4Instr::ConvertBoolToInt; break; - case IR::RealType: { + case IR::NumberType: { if (s->isMoveForReturn) - opcode = V4Instr::MathRoundReal; + opcode = V4Instr::MathRoundNumber; else - opcode = V4Instr::ConvertRealToInt; + opcode = V4Instr::ConvertNumberToInt; break; } case IR::StringType: opcode = V4Instr::ConvertStringToInt; break; default: break; } // switch - } else if (targetTy == IR::RealType) { + } else if (IR::isRealType(targetTy)) { switch (sourceTy) { - case IR::BoolType: opcode = V4Instr::ConvertBoolToReal; break; - case IR::IntType: opcode = V4Instr::ConvertIntToReal; break; - case IR::StringType: opcode = V4Instr::ConvertStringToReal; break; + case IR::BoolType: opcode = V4Instr::ConvertBoolToNumber; break; + case IR::IntType: opcode = V4Instr::ConvertIntToNumber; break; + case IR::StringType: opcode = V4Instr::ConvertStringToNumber; break; default: break; } // switch } else if (targetTy == IR::StringType) { switch (sourceTy) { case IR::BoolType: opcode = V4Instr::ConvertBoolToString; break; case IR::IntType: opcode = V4Instr::ConvertIntToString; break; - case IR::RealType: opcode = V4Instr::ConvertRealToString; break; + case IR::NumberType: opcode = V4Instr::ConvertNumberToString; break; case IR::UrlType: opcode = V4Instr::ConvertUrlToString; break; case IR::ColorType: opcode = V4Instr::ConvertColorToString; break; default: break; } // switch } else if (targetTy == IR::UrlType) { + if (s->isMoveForReturn && sourceTy != IR::StringType) { + V4Instr instr; + instr.throwop.exceptionId = exceptionId(bindingLine, bindingColumn); + registerLiteralString(dest, _function->newString(QString::fromUtf8("Unable to assign %1 to %2") + .arg(QLatin1String(IR::typeName(sourceTy))) + .arg(QLatin1String(IR::typeName(targetTy))))); + instr.throwop.message = dest; + gen(V4Instr::Throw, instr); + return; + } + V4Instr convToString; convToString.unaryop.output = dest; convToString.unaryop.src = src; @@ -998,7 +1051,7 @@ void QV4CompilerPrivate::visitMove(IR::Move *s) switch (sourceTy) { case IR::BoolType: gen(V4Instr::ConvertBoolToString, convToString); sourceTy = IR::StringType; break; case IR::IntType: gen(V4Instr::ConvertIntToString, convToString); sourceTy = IR::StringType; break; - case IR::RealType: gen(V4Instr::ConvertRealToString, convToString); sourceTy = IR::StringType; break; + case IR::NumberType: gen(V4Instr::ConvertNumberToString, convToString); sourceTy = IR::StringType; break; case IR::ColorType: gen(V4Instr::ConvertColorToString, convToString); sourceTy = IR::StringType; break; default: break; } // switch @@ -1094,8 +1147,9 @@ void QV4CompilerPrivate::visitRet(IR::Ret *s) case IR::IntType: test.regType = QMetaType::Int; break; - case IR::RealType: - test.regType = QMetaType::QReal; + case IR::FloatType: + case IR::NumberType: + test.regType = QMetaType::Double; break; default: discard(); @@ -1108,6 +1162,7 @@ void QV4CompilerPrivate::visitRet(IR::Ret *s) store.output = 0; store.index = expression->property->index; store.reg = storeReg; + store.valueType = s->type == IR::FloatType ? FloatType : 0; store.exceptionId = exceptionId(s->line, s->column); gen(store); } @@ -1119,7 +1174,6 @@ void QV4Compiler::dump(const QByteArray &programData) qWarning() << "Program.bindings:" << program->bindings; qWarning() << "Program.dataLength:" << program->dataLength; qWarning() << "Program.subscriptions:" << program->subscriptions; - qWarning() << "Program.indentifiers:" << program->identifiers; const int programSize = program->instructionCount; const char *start = program->instructions(); @@ -1137,8 +1191,8 @@ void QV4CompilerPrivate::resetInstanceState() data = committed.data; exceptions = committed.exceptions; usedSubscriptionIds.clear(); - subscriptionIds = committed.subscriptionIds; - registeredStrings = committed.registeredStrings; + subscriptionIds.clear(); + subscriptionOffset = committed.subscriptionCount; bytecode.clear(); patches.clear(); pool.clear(); @@ -1159,8 +1213,9 @@ int QV4CompilerPrivate::commitCompile() committed.bytecode.append(bytecode.constData(), bytecode.size()); committed.data = data; committed.exceptions = exceptions; - committed.subscriptionIds = subscriptionIds; - committed.registeredStrings = registeredStrings; + committed.subscriptionCount = subscriptionOffset + subscriptionIds.count(); + if (bindingsDump()) + committed.subscriptions.append(subscriptionIds); return rv; } @@ -1212,7 +1267,7 @@ bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node) qerr << endl; } - if (discarded || subscriptionIds.count() > 0xFFFF || registeredStrings.count() > 0xFFFF || registerCount > 31) + if (discarded || subscriptionIds.count() > 0xFFFF || registerCount > 31) return false; return true; @@ -1236,32 +1291,6 @@ int QV4CompilerPrivate::registerLiteralString(quint8 reg, const QStringRef &str) return reg; } -// Returns an identifier offset -int QV4CompilerPrivate::registerString(const QString &string) -{ - Q_ASSERT(!string.isEmpty()); - - QPair<int, int> *iter = registeredStrings.value(string); - - if (!iter) { - quint32 len = string.length(); - QByteArray lendata((const char *)&len, sizeof(quint32)); - QByteArray strdata((const char *)string.constData(), string.length() * sizeof(QChar)); - strdata.prepend(lendata); - int rv = data.count(); - data += strdata; - - iter = ®isteredStrings[string]; - *iter = qMakePair(registeredStrings.count(), rv); - } - - Instr::InitString reg; - reg.offset = iter->first; - reg.dataIdx = iter->second; - gen(reg); - return reg.offset; -} - /*! Returns true if the current expression has not already subscribed to \a sub in currentBlockMask. */ @@ -1285,7 +1314,7 @@ int QV4CompilerPrivate::subscriptionIndex(const QStringList &sub) QString str = sub.join(QLatin1String(".")); int *iter = subscriptionIds.value(str); if (!iter) { - int count = subscriptionIds.count(); + int count = subscriptionOffset + subscriptionIds.count(); iter = &subscriptionIds[str]; *iter = count; } @@ -1384,8 +1413,8 @@ QByteArray QV4CompilerPrivate::buildSignalTable() const QVector<quint32> header; QVector<quint32> data; - for (int ii = 0; ii < committed.subscriptionIds.count(); ++ii) { - header.append(committed.subscriptionIds.count() + data.count()); + for (int ii = 0; ii < committed.subscriptionCount; ++ii) { + header.append(committed.subscriptionCount + data.count()); const QList<QPair<int, quint32> > &bindings = table[ii]; data.append(bindings.count()); for (int jj = 0; jj < bindings.count(); ++jj) { @@ -1443,8 +1472,7 @@ QByteArray QV4Compiler::program() const data += d->buildExceptionData(); prog.dataLength = 4 * ((data.size() + 3) / 4); - prog.subscriptions = d->committed.subscriptionIds.count(); - prog.identifiers = d->committed.registeredStrings.count(); + prog.subscriptions = d->committed.subscriptionCount; prog.instructionCount = bytecode.count(); int size = sizeof(QV4Program) + bytecode.count(); size += prog.dataLength; @@ -1461,12 +1489,13 @@ QByteArray QV4Compiler::program() const if (bindingsDump()) { qWarning().nospace() << "Subscription slots:"; - for (QQmlAssociationList<QString, int>::ConstIterator iter = d->committed.subscriptionIds.begin(); - iter != d->committed.subscriptionIds.end(); - ++iter) { - qWarning().nospace() << " " << iter->first << "\t-> " << iter->second; + QQmlAssociationList<QString, int> subscriptionIds; + foreach (subscriptionIds, d->committed.subscriptions) { + for (QQmlAssociationList<QString, int>::ConstIterator iter = subscriptionIds.begin(); + iter != subscriptionIds.end(); ++iter) { + qWarning().nospace() << " " << iter->first << "\t-> " << iter->second; + } } - QV4Compiler::dump(programData); } diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h index a9209d978f..0c06ade87f 100644 --- a/src/qml/qml/v4/qv4compiler_p_p.h +++ b/src/qml/qml/v4/qv4compiler_p_p.h @@ -128,8 +128,6 @@ public: bool compile(QQmlJS::AST::Node *); int registerLiteralString(quint8 reg, const QStringRef &); - int registerString(const QString &); - QQmlAssociationList<QString, QPair<int, int> > registeredStrings; QByteArray data; bool blockNeedsSubscription(const QStringList &); @@ -141,7 +139,7 @@ public: QVector<quint64> exceptions; QQmlAssociationList<int, quint32> usedSubscriptionIds; - + int subscriptionOffset; QQmlAssociationList<QString, int> subscriptionIds; QQmlJS::Bytecode bytecode; @@ -156,17 +154,17 @@ public: QQmlPool pool; // Committed binding data - struct { + struct Committed { + Committed(): subscriptionCount(0) {} QList<int> offsets; QList<QQmlAssociationList<int, quint32> > dependencies; //QQmlJS::Bytecode bytecode; QByteArray bytecode; QByteArray data; - QQmlAssociationList<QString, int> subscriptionIds; QVector<quint64> exceptions; - - QQmlAssociationList<QString, QPair<int, int> > registeredStrings; + int subscriptionCount; + QList<QQmlAssociationList<QString, int> > subscriptions; int count() const { return offsets.count(); } } committed; @@ -174,7 +172,7 @@ public: QByteArray buildSignalTable() const; QByteArray buildExceptionData() const; - void convertToReal(QQmlJS::IR::Expr *expr, int reg); + void convertToNumber(QQmlJS::IR::Expr *expr, int reg); void convertToInt(QQmlJS::IR::Expr *expr, int reg); void convertToBool(QQmlJS::IR::Expr *expr, int reg); quint8 instructionOpcode(QQmlJS::IR::Binop *e); @@ -235,6 +233,8 @@ private: bool usedSubscriptionIdsChanged; quint32 currentBlockMask; + int bindingLine; + int bindingColumn; }; diff --git a/src/qml/qml/v4/qv4instruction.cpp b/src/qml/qml/v4/qv4instruction.cpp index cb6ff40589..ecd4f01ef3 100644 --- a/src/qml/qml/v4/qv4instruction.cpp +++ b/src/qml/qml/v4/qv4instruction.cpp @@ -123,14 +123,14 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::UnaryNot: INSTR_DUMP << "\t" << "UnaryNot" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::UnaryMinusReal: - INSTR_DUMP << "\t" << "UnaryMinusReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::UnaryMinusNumber: + INSTR_DUMP << "\t" << "UnaryMinusNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::UnaryMinusInt: INSTR_DUMP << "\t" << "UnaryMinusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::UnaryPlusReal: - INSTR_DUMP << "\t" << "UnaryPlusReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::UnaryPlusNumber: + INSTR_DUMP << "\t" << "UnaryPlusNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::UnaryPlusInt: INSTR_DUMP << "\t" << "UnaryPlusInt" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; @@ -138,8 +138,8 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::ConvertBoolToInt: INSTR_DUMP << "\t" << "ConvertBoolToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::ConvertBoolToReal: - INSTR_DUMP << "\t" << "ConvertBoolToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::ConvertBoolToNumber: + INSTR_DUMP << "\t" << "ConvertBoolToNumber" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::ConvertBoolToString: INSTR_DUMP << "\t" << "ConvertBoolToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; @@ -147,20 +147,20 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::ConvertIntToBool: INSTR_DUMP << "\t" << "ConvertIntToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::ConvertIntToReal: - INSTR_DUMP << "\t" << "ConvertIntToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::ConvertIntToNumber: + INSTR_DUMP << "\t" << "ConvertIntToNumber" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::ConvertIntToString: INSTR_DUMP << "\t" << "ConvertIntToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::ConvertRealToBool: - INSTR_DUMP << "\t" << "ConvertRealToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::ConvertNumberToBool: + INSTR_DUMP << "\t" << "ConvertNumberToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::ConvertRealToInt: - INSTR_DUMP << "\t" << "ConvertRealToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::ConvertNumberToInt: + INSTR_DUMP << "\t" << "ConvertNumberToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::ConvertRealToString: - INSTR_DUMP << "\t" << "ConvertRealToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::ConvertNumberToString: + INSTR_DUMP << "\t" << "ConvertNumberToString" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::ConvertStringToBool: INSTR_DUMP << "\t" << "ConvertStringToBool" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; @@ -168,8 +168,8 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::ConvertStringToInt: INSTR_DUMP << "\t" << "ConvertStringToInt" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::ConvertStringToReal: - INSTR_DUMP << "\t" << "ConvertStringToReal" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::ConvertStringToNumber: + INSTR_DUMP << "\t" << "ConvertStringToNumber" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::ConvertStringToUrl: INSTR_DUMP << "\t" << "ConvertStringToUrl" << "\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; @@ -198,32 +198,32 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::ResolveUrl: INSTR_DUMP << "\t" << "ResolveUrl" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathSinReal: - INSTR_DUMP << "\t" << "MathSinReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathSinNumber: + INSTR_DUMP << "\t" << "MathSinNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathCosReal: - INSTR_DUMP << "\t" << "MathCosReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathCosNumber: + INSTR_DUMP << "\t" << "MathCosNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathAbsReal: - INSTR_DUMP << "\t" << "MathAbsReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathAbsNumber: + INSTR_DUMP << "\t" << "MathAbsNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathRoundReal: - INSTR_DUMP << "\t" << "MathRoundReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathRoundNumber: + INSTR_DUMP << "\t" << "MathRoundNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathFloorReal: - INSTR_DUMP << "\t" << "MathFloorReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathFloorNumber: + INSTR_DUMP << "\t" << "MathFloorNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathCeilReal: - INSTR_DUMP << "\t" << "MathCeilReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathCeilNumber: + INSTR_DUMP << "\t" << "MathCeilNumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; - case V4Instr::MathPIReal: - INSTR_DUMP << "\t" << "MathPIReal" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; + case V4Instr::MathPINumber: + INSTR_DUMP << "\t" << "MathPINumber" << "\t\t" << "Input_Reg(" << i->unaryop.src << ") -> Output_Reg(" << i->unaryop.output << ")"; break; case V4Instr::LoadNull: INSTR_DUMP << "\t" << "LoadNull" << "\t\t" << "Constant(null) -> Output_Reg(" << i->null_value.reg << ")"; break; - case V4Instr::LoadReal: - INSTR_DUMP << "\t" << "LoadReal" << "\t\t" << "Constant(" << i->real_value.value << ") -> Output_Reg(" << i->real_value.reg << ")"; + case V4Instr::LoadNumber: + INSTR_DUMP << "\t" << "LoadNumber" << "\t\t" << "Constant(" << i->number_value.value << ") -> Output_Reg(" << i->number_value.reg << ")"; break; case V4Instr::LoadInt: INSTR_DUMP << "\t" << "LoadInt" << "\t\t\t" << "Constant(" << i->int_value.value << ") -> Output_Reg(" << i->int_value.reg << ")"; @@ -249,23 +249,23 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::BitXorInt: INSTR_DUMP << "\t" << "BitXorInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::AddReal: - INSTR_DUMP << "\t" << "AddReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::AddNumber: + INSTR_DUMP << "\t" << "AddNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; case V4Instr::AddString: INSTR_DUMP << "\t" << "AddString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::SubReal: - INSTR_DUMP << "\t" << "SubReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::SubNumber: + INSTR_DUMP << "\t" << "SubNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::MulReal: - INSTR_DUMP << "\t" << "MulReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::MulNumber: + INSTR_DUMP << "\t" << "MulNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::DivReal: - INSTR_DUMP << "\t" << "DivReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::DivNumber: + INSTR_DUMP << "\t" << "DivNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::ModReal: - INSTR_DUMP << "\t" << "ModReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::ModNumber: + INSTR_DUMP << "\t" << "ModNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; case V4Instr::LShiftInt: INSTR_DUMP << "\t" << "LShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; @@ -276,29 +276,29 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::URShiftInt: INSTR_DUMP << "\t" << "URShiftInt" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::GtReal: - INSTR_DUMP << "\t" << "GtReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::GtNumber: + INSTR_DUMP << "\t" << "GtNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::LtReal: - INSTR_DUMP << "\t" << "LtReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::LtNumber: + INSTR_DUMP << "\t" << "LtNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::GeReal: - INSTR_DUMP << "\t" << "GeReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::GeNumber: + INSTR_DUMP << "\t" << "GeNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::LeReal: - INSTR_DUMP << "\t" << "LeReal" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::LeNumber: + INSTR_DUMP << "\t" << "LeNumber" << "\t\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::EqualReal: - INSTR_DUMP << "\t" << "EqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::EqualNumber: + INSTR_DUMP << "\t" << "EqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::NotEqualReal: - INSTR_DUMP << "\t" << "NotEqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::NotEqualNumber: + INSTR_DUMP << "\t" << "NotEqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::StrictEqualReal: - INSTR_DUMP << "\t" << "StrictEqualReal" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::StrictEqualNumber: + INSTR_DUMP << "\t" << "StrictEqualNumber" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::StrictNotEqualReal: - INSTR_DUMP << "\t" << "StrictNotEqualReal" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::StrictNotEqualNumber: + INSTR_DUMP << "\t" << "StrictNotEqualNumber" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; case V4Instr::GtString: INSTR_DUMP << "\t" << "GtString" << "\t\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; @@ -336,11 +336,11 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::StrictNotEqualObject: INSTR_DUMP << "\t" << "StrictNotEqualObject" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::MathMaxReal: - INSTR_DUMP << "\t" << "MathMaxReal" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::MathMaxNumber: + INSTR_DUMP << "\t" << "MathMaxNumber" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; - case V4Instr::MathMinReal: - INSTR_DUMP << "\t" << "MathMinReal" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; + case V4Instr::MathMinNumber: + INSTR_DUMP << "\t" << "MathMinNumber" << "\t" << "Input_Reg(" << i->binaryop.left << ") Input_Reg(" << i->binaryop.right << ") -> Output_Reg(" << i->binaryop.output << ")"; break; case V4Instr::NewString: INSTR_DUMP << "\t" << "NewString" << "\t\t" << "Register(" << i->construct.reg << ")"; @@ -376,12 +376,12 @@ void Bytecode::dump(const V4Instr *i, int address) const case V4Instr::Branch: INSTR_DUMP << "\t" << "Branch" << "\t\t\t" << "Address(" << (address + size() + i->branchop.offset) << ")"; break; - case V4Instr::InitString: - INSTR_DUMP << "\t" << "InitString" << "\t\t" << "String_DataIndex(" << i->initstring.dataIdx << ") -> String_Slot(" << i->initstring.offset << ")"; - break; case V4Instr::Block: INSTR_DUMP << "\t" << "Block" << "\t\t\t" << "Mask(" << QByteArray::number(i->blockop.block, 16).constData() << ")"; break; + case V4Instr::Throw: + INSTR_DUMP << "\t" << "Throw" << "\t\t\t" << "InputReg(" << i->throwop.message << ")"; + break; default: INSTR_DUMP << "\t" << "Unknown"; break; diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h index 239cb362cc..310bfbaff5 100644 --- a/src/qml/qml/v4/qv4instruction_p.h +++ b/src/qml/qml/v4/qv4instruction_p.h @@ -76,22 +76,22 @@ QT_BEGIN_NAMESPACE F(LoadModuleObject, load) \ F(LoadAttached, attached) \ F(UnaryNot, unaryop) \ - F(UnaryMinusReal, unaryop) \ + F(UnaryMinusNumber, unaryop) \ F(UnaryMinusInt, unaryop) \ - F(UnaryPlusReal, unaryop) \ + F(UnaryPlusNumber, unaryop) \ F(UnaryPlusInt, unaryop) \ F(ConvertBoolToInt, unaryop) \ - F(ConvertBoolToReal, unaryop) \ + F(ConvertBoolToNumber, unaryop) \ F(ConvertBoolToString, unaryop) \ F(ConvertIntToBool, unaryop) \ - F(ConvertIntToReal, unaryop) \ + F(ConvertIntToNumber, unaryop) \ F(ConvertIntToString, unaryop) \ - F(ConvertRealToBool, unaryop) \ - F(ConvertRealToInt, unaryop) \ - F(ConvertRealToString, unaryop) \ + F(ConvertNumberToBool, unaryop) \ + F(ConvertNumberToInt, unaryop) \ + F(ConvertNumberToString, unaryop) \ F(ConvertStringToBool, unaryop) \ F(ConvertStringToInt, unaryop) \ - F(ConvertStringToReal, unaryop) \ + F(ConvertStringToNumber, unaryop) \ F(ConvertStringToUrl, unaryop) \ F(ConvertStringToColor, unaryop) \ F(ConvertUrlToBool, unaryop) \ @@ -101,15 +101,15 @@ QT_BEGIN_NAMESPACE F(ConvertObjectToBool, unaryop) \ F(ConvertNullToObject, unaryop) \ F(ResolveUrl, unaryop) \ - F(MathSinReal, unaryop) \ - F(MathCosReal, unaryop) \ - F(MathAbsReal, unaryop) \ - F(MathRoundReal, unaryop) \ - F(MathFloorReal, unaryop) \ - F(MathCeilReal, unaryop) \ - F(MathPIReal, unaryop) \ + F(MathSinNumber, unaryop) \ + F(MathCosNumber, unaryop) \ + F(MathAbsNumber, unaryop) \ + F(MathRoundNumber, unaryop) \ + F(MathFloorNumber, unaryop) \ + F(MathCeilNumber, unaryop) \ + F(MathPINumber, unaryop) \ F(LoadNull, null_value) \ - F(LoadReal, real_value) \ + F(LoadNumber, number_value) \ F(LoadInt, int_value) \ F(LoadBool, bool_value) \ F(LoadString, string_value) \ @@ -118,23 +118,23 @@ QT_BEGIN_NAMESPACE F(BitAndInt, binaryop) \ F(BitOrInt, binaryop) \ F(BitXorInt, binaryop) \ - F(AddReal, binaryop) \ + F(AddNumber, binaryop) \ F(AddString, binaryop) \ - F(SubReal, binaryop) \ - F(MulReal, binaryop) \ - F(DivReal, binaryop) \ - F(ModReal, binaryop) \ + F(SubNumber, binaryop) \ + F(MulNumber, binaryop) \ + F(DivNumber, binaryop) \ + F(ModNumber, binaryop) \ F(LShiftInt, binaryop) \ F(RShiftInt, binaryop) \ F(URShiftInt, binaryop) \ - F(GtReal, binaryop) \ - F(LtReal, binaryop) \ - F(GeReal, binaryop) \ - F(LeReal, binaryop) \ - F(EqualReal, binaryop) \ - F(NotEqualReal, binaryop) \ - F(StrictEqualReal, binaryop) \ - F(StrictNotEqualReal, binaryop) \ + F(GtNumber, binaryop) \ + F(LtNumber, binaryop) \ + F(GeNumber, binaryop) \ + F(LeNumber, binaryop) \ + F(EqualNumber, binaryop) \ + F(NotEqualNumber, binaryop) \ + F(StrictEqualNumber, binaryop) \ + F(StrictNotEqualNumber, binaryop) \ F(GtString, binaryop) \ F(LtString, binaryop) \ F(GeString, binaryop) \ @@ -147,8 +147,8 @@ QT_BEGIN_NAMESPACE F(NotEqualObject, binaryop) \ F(StrictEqualObject, binaryop) \ F(StrictNotEqualObject, binaryop) \ - F(MathMaxReal, binaryop) \ - F(MathMinReal, binaryop) \ + F(MathMaxNumber, binaryop) \ + F(MathMinNumber, binaryop) \ F(NewString, construct) \ F(NewUrl, construct) \ F(CleanupRegister, cleanup) \ @@ -160,8 +160,7 @@ QT_BEGIN_NAMESPACE F(BranchFalse, branchop) \ F(Branch, branchop) \ F(Block, blockop) \ - /* Speculative property resolution */ \ - F(InitString, initstring) + F(Throw, throwop) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define QML_THREADED_INTERPRETER @@ -192,7 +191,7 @@ class QQmlNotifier; namespace QQmlJS { -union V4Instr { +union Q_AUTOTEST_EXPORT V4Instr { enum Type { FOR_EACH_V4_INSTR(QML_V4_INSTR_ENUM) }; @@ -241,6 +240,7 @@ union V4Instr { qint8 output; qint8 reg; quint8 exceptionId; + quint8 valueType; quint32 index; }; @@ -283,10 +283,10 @@ union V4Instr { qint8 reg; }; - struct instr_real_value { + struct instr_number_value { QML_V4_INSTR_HEADER qint8 reg; - qreal value; // XXX Makes the instruction 12 bytes + double value; // XXX Makes the instruction 12 bytes }; struct instr_int_value { @@ -358,6 +358,12 @@ union V4Instr { quint32 block; }; + struct instr_throwop { + QML_V4_INSTR_HEADER + quint8 exceptionId; + quint32 message; + }; + instr_common common; instr_id id; instr_init init; @@ -371,7 +377,7 @@ union V4Instr { instr_copy copy; instr_construct construct; instr_null_value null_value; - instr_real_value real_value; + instr_number_value number_value; instr_int_value int_value; instr_bool_value bool_value; instr_string_value string_value; @@ -383,6 +389,7 @@ union V4Instr { instr_initstring initstring; instr_branchop branchop; instr_blockop blockop; + instr_throwop throwop; }; template<int N> @@ -400,11 +407,11 @@ FOR_EACH_V4_INSTR(QML_V4_INSTR_META_TEMPLATE); #undef QML_V4_INSTR_META_TEMPLATE template<int Instr> -class V4InstrData : public V4InstrMeta<Instr>::DataType +class Q_AUTOTEST_EXPORT V4InstrData : public V4InstrMeta<Instr>::DataType { }; -class Bytecode +class Q_AUTOTEST_EXPORT Bytecode { Q_DISABLE_COPY(Bytecode) diff --git a/src/qml/qml/v4/qv4ir.cpp b/src/qml/qml/v4/qv4ir.cpp index 34245f5bf4..ba0faec80e 100644 --- a/src/qml/qml/v4/qv4ir.cpp +++ b/src/qml/qml/v4/qv4ir.cpp @@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { namespace IR { -inline const char *typeName(Type t) +const char *typeName(Type t) { switch (t) { case InvalidType: return "invalid"; @@ -58,15 +58,15 @@ inline const char *typeName(Type t) case NullType: return "null"; case VoidType: return "void"; case StringType: return "string"; - case UrlType: return "url"; - case ColorType: return "color"; + case UrlType: return "QUrl"; + case ColorType: return "QColor"; case SGAnchorLineType: return "SGAnchorLine"; case AttachType: return "AttachType"; case ObjectType: return "object"; case BoolType: return "bool"; case IntType: return "int"; - case RealType: return "qreal"; - case RealNaNType: return "NaN"; + case FloatType: return "float"; + case NumberType: return "number"; default: return "invalid"; } } @@ -91,15 +91,20 @@ IR::Type maxType(IR::Type left, IR::Type right) return IR::StringType; } else if (left == right) return left; - else if (isNumberType(left) && isNumberType(right)) - return qMax(left, right); - else if ((isNumberType(left) && isStringType(right)) || + else if (isNumberType(left) && isNumberType(right)) { + IR::Type ty = qMax(left, right); + return ty == FloatType ? NumberType : ty; // promote floats + } else if ((isNumberType(left) && isStringType(right)) || (isNumberType(right) && isStringType(left))) return IR::StringType; else return IR::InvalidType; } +bool isRealType(IR::Type type) +{ + return type == IR::NumberType || type == IR::FloatType; +} const char *opname(AluOp op) { @@ -233,7 +238,7 @@ void Name::init(Name *base, Type type, const QString *id, Symbol symbol, quint32 builtin = MathMinBuiltinFunction; } else if (id->length() == 7 && *id == QLatin1String("Math.PI")) { builtin = MathPIBuiltinConstant; - this->type = RealType; + this->type = NumberType; } } @@ -267,7 +272,7 @@ Type Unop::typeForOp(AluOp op, Expr *expr) case OpUMinus: case OpUPlus: case OpCompl: - return maxType(expr->type, RealType); + return maxType(expr->type, NumberType); default: break; @@ -309,13 +314,13 @@ Type Binop::typeForOp(AluOp op, Expr *left, Expr *right) case OpAdd: if (left->type == StringType) return StringType; - return RealType; + return NumberType; case OpSub: case OpMul: case OpDiv: case OpMod: - return RealType; + return NumberType; case OpLShift: case OpRShift: @@ -364,7 +369,7 @@ Type Call::typeForFunction(Expr *base) case MathAbsBuiltinFunction: //### type could also be Int if input was Int case MathMaxBuiltinFunction: case MathMinBuiltinFunction: - return RealType; + return NumberType; case MathRoundBultinFunction: case MathFloorBultinFunction: @@ -601,6 +606,12 @@ Expr *BasicBlock::BINOP(AluOp op, Expr *left, Expr *right) break; } } + } else if (op == OpAdd) { + if (String *s1 = left->asString()) { + if (String *s2 = right->asString()) { + return STRING(function->newString(s1->value.toString() + s2->value)); + } + } } } diff --git a/src/qml/qml/v4/qv4ir_p.h b/src/qml/qml/v4/qv4ir_p.h index 4e9f9faacd..26bd43c406 100644 --- a/src/qml/qml/v4/qv4ir_p.h +++ b/src/qml/qml/v4/qv4ir_p.h @@ -150,10 +150,12 @@ enum Type { FirstNumberType, BoolType = FirstNumberType, IntType, - RealType, - RealNaNType + FloatType, + NumberType }; Type maxType(IR::Type left, IR::Type right); +bool isRealType(IR::Type type); +const char *typeName(IR::Type t); struct ExprVisitor { virtual ~ExprVisitor() {} diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp index 453120c6c7..31ed9a5a6a 100644 --- a/src/qml/qml/v4/qv4irbuilder.cpp +++ b/src/qml/qml/v4/qv4irbuilder.cpp @@ -61,8 +61,11 @@ static IR::Type irTypeFromVariantType(int t, QQmlEnginePrivate *engine, const QM case QMetaType::Int: return IR::IntType; - case QMetaType::QReal: - return IR::RealType; + case QMetaType::Float: + return IR::FloatType; + + case QMetaType::Double: + return IR::NumberType; case QMetaType::QString: return IR::StringType; @@ -542,7 +545,7 @@ bool QV4IRBuilder::visit(AST::NumericLiteral *ast) _expr.format = ExprResult::cx; _block->JUMP(ast->value ? _expr.iftrue : _expr.iffalse); } else { - _expr.code = _block->CONST(IR::RealType, ast->value); + _expr.code = _block->CONST(IR::NumberType, ast->value); } return false; } @@ -889,7 +892,14 @@ void QV4IRBuilder::binop(AST::BinaryExpression *ast, ExprResult left, ExprResult _expr.format = ExprResult::cx; _block->CJUMP(_block->BINOP(IR::binaryOperator(ast->op), left, right), _expr.iftrue, _expr.iffalse); } else { - _expr.code = _block->BINOP(IR::binaryOperator(ast->op), left, right); + IR::Expr *e = _block->BINOP(IR::binaryOperator(ast->op), left, right); + if (e->asConst() != 0 || e->asString() != 0) + _expr.code = e; + else { + IR::Temp *t = _block->TEMP(e->type); + _block->MOVE(t, e); + _expr.code = t; + } } } @@ -977,8 +987,8 @@ bool QV4IRBuilder::visit(AST::BinaryExpression *ast) if (left.type() == IR::StringType && right.type() == IR::StringType) { binop(ast, left, right); } else if (left.isValid() && right.isValid()) { - implicitCvt(left, IR::RealType); - implicitCvt(right, IR::RealType); + implicitCvt(left, IR::NumberType); + implicitCvt(right, IR::NumberType); binop(ast, left, right); } } break; @@ -998,8 +1008,8 @@ bool QV4IRBuilder::visit(AST::BinaryExpression *ast) } } else if ((left.type() == IR::StringType && right.type() >= IR::FirstNumberType) || (left.type() >= IR::FirstNumberType && right.type() == IR::StringType)) { - implicitCvt(left, IR::RealType); - implicitCvt(right, IR::RealType); + implicitCvt(left, IR::NumberType); + implicitCvt(right, IR::NumberType); binop(ast, left, right); } else if (left.isValid() && right.isValid()) { binop(ast, left, right); @@ -1086,8 +1096,8 @@ bool QV4IRBuilder::visit(AST::BinaryExpression *ast) IR::Type t = maxType(left.type(), right.type()); if (t >= IR::FirstNumberType) { - implicitCvt(left, IR::RealType); - implicitCvt(right, IR::RealType); + implicitCvt(left, IR::NumberType); + implicitCvt(right, IR::NumberType); IR::Expr *code = _block->BINOP(IR::binaryOperator(ast->op), left, right); _expr.code = _block->TEMP(code->type); diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h index 2b338c0778..e73ec22750 100644 --- a/src/qml/qml/v4/qv4irbuilder_p.h +++ b/src/qml/qml/v4/qv4irbuilder_p.h @@ -95,8 +95,8 @@ protected: case QQmlJS::IR::StringType: case QQmlJS::IR::BoolType: case QQmlJS::IR::IntType: - case QQmlJS::IR::RealType: - case QQmlJS::IR::RealNaNType: + case QQmlJS::IR::FloatType: + case QQmlJS::IR::NumberType: return true; default: diff --git a/src/qml/qml/v4/qv4program_p.h b/src/qml/qml/v4/qv4program_p.h index 60e7403786..c1dc39279d 100644 --- a/src/qml/qml/v4/qv4program_p.h +++ b/src/qml/qml/v4/qv4program_p.h @@ -65,7 +65,6 @@ struct QV4Program { quint32 signalTableOffset; quint32 exceptionDataOffset; quint16 subscriptions; - quint16 identifiers; quint16 instructionCount; struct BindingReference { @@ -87,7 +86,8 @@ enum QQmlRegisterType { UndefinedType, NullType, QObjectStarType, - QRealType, + NumberType, + FloatType, IntType, BoolType, diff --git a/src/qml/qml/v8/qjsvalue_p.h b/src/qml/qml/v8/qjsvalue_p.h index acfe958cb6..099d53ee7a 100644 --- a/src/qml/qml/v8/qjsvalue_p.h +++ b/src/qml/qml/v8/qjsvalue_p.h @@ -177,7 +177,7 @@ private: CBool, CNull, CUndefined, - JSValue = 0x2000, // V8 values are equal or higher then this value. + JSValue = 0x2000 // V8 values are equal or higher then this value. // JSPrimitive, // JSObject } m_state; diff --git a/src/qml/qml/v8/qv8bindings.cpp b/src/qml/qml/v8/qv8bindings.cpp index 4b96679cf3..025854f1ac 100644 --- a/src/qml/qml/v8/qv8bindings.cpp +++ b/src/qml/qml/v8/qv8bindings.cpp @@ -58,7 +58,7 @@ static QQmlJavaScriptExpression::VTable QV8Bindings_Binding_jsvtable = { }; QV8Bindings::Binding::Binding() -: QQmlJavaScriptExpression(&QV8Bindings_Binding_jsvtable), target(0), parent(0) +: QQmlJavaScriptExpression(&QV8Bindings_Binding_jsvtable), parent(0) { } @@ -85,12 +85,20 @@ void QV8Bindings::Binding::refresh() int QV8Bindings::Binding::propertyIndex() const { - return instruction->property.encodedIndex(); + if (target.hasValue()) return target.constValue()->targetProperty; + else return instruction->property.encodedIndex(); } QObject *QV8Bindings::Binding::object() const { - return target; + if (target.hasValue()) return target.constValue()->target; + else return *target; +} + +void QV8Bindings::Binding::retargetBinding(QObject *t, int i) +{ + target.value().target = t; + target.value().targetProperty = i; } void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) @@ -127,13 +135,13 @@ void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) trace.event("writing V8 result"); bool needsErrorData = false; - if (!watcher.wasDeleted() && !hasError()) { + if (!watcher.wasDeleted() && !destroyedFlag() && !hasError()) { typedef QQmlPropertyPrivate PP; - needsErrorData = !PP::writeBinding(target, instruction->property, context, this, result, + needsErrorData = !PP::writeBinding(*target, instruction->property, context, this, result, isUndefined, flags); } - if (!watcher.wasDeleted()) { + if (!watcher.wasDeleted() && !destroyedFlag()) { if (needsErrorData) { QUrl url = parent->url(); @@ -156,7 +164,7 @@ void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) ep->dereferenceScarceResources(); } else { - QQmlProperty p = QQmlPropertyPrivate::restore(target, instruction->property, context); + QQmlProperty p = QQmlPropertyPrivate::restore(*target, instruction->property, context); QQmlAbstractBinding::printBindingLoopError(p); } } @@ -177,6 +185,7 @@ void QV8Bindings::Binding::expressionChanged(QQmlJavaScriptExpression *e) void QV8Bindings::Binding::destroy() { setEnabledFlag(false); + setDestroyedFlag(true); removeFromObject(); clear(); clearError(); diff --git a/src/qml/qml/v8/qv8bindings_p.h b/src/qml/qml/v8/qv8bindings_p.h index ad5b2cb8b0..7cc1cc9c21 100644 --- a/src/qml/qml/v8/qv8bindings_p.h +++ b/src/qml/qml/v8/qv8bindings_p.h @@ -53,12 +53,13 @@ // We mean it. // +#include <private/qpointervaluepair_p.h> #include <private/qqmlpropertycache_p.h> #include <private/qqmlinstruction_p.h> #include <private/qqmlexpression_p.h> #include <private/qqmlcompiler_p.h> -#include <private/qqmlbinding_p.h> #include <private/qflagpointer_p.h> +#include <private/qqmlbinding_p.h> QT_BEGIN_HEADER @@ -96,17 +97,26 @@ public: virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); virtual void update(QQmlPropertyPrivate::WriteFlags flags); virtual void destroy(); - virtual int propertyIndex() const; virtual QObject *object() const; + virtual int propertyIndex() const; + virtual void retargetBinding(QObject *, int); - QObject *target; QV8Bindings *parent; + struct Retarget { + QObject *target; + int targetProperty; + }; + // To save memory, we store flags inside the instruction pointer. - // flag1: enabled - // flag2: updating + // target.flag1: destroyed + // instruction.flag1: enabled + // instruction.flag2: updating + QPointerValuePair<QObject, Retarget> target; QFlagPointer<const QQmlInstruction::instr_assignBinding> instruction; + inline bool destroyedFlag() const { return target.flag(); } + inline void setDestroyedFlag(bool v) { return target.setFlagValue(v); } inline bool enabledFlag() const { return instruction.flag(); } inline void setEnabledFlag(bool v) { instruction.setFlagValue(v); } inline bool updatingFlag() const { return instruction.flag2(); } diff --git a/src/qml/qml/v8/qv8contextwrapper.cpp b/src/qml/qml/v8/qv8contextwrapper.cpp index 39392b8984..8a98727205 100644 --- a/src/qml/qml/v8/qv8contextwrapper.cpp +++ b/src/qml/qml/v8/qv8contextwrapper.cpp @@ -327,7 +327,7 @@ v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property, const QVariant &value = cp->propertyValues.at(propertyIdx); if (value.userType() == qMetaTypeId<QList<QObject*> >()) { - QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*)propertyIdx, + QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*) qintptr(propertyIdx), 0, QQmlContextPrivate::context_count, QQmlContextPrivate::context_at); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 678f9aa3ee..2302d0e369 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -46,7 +46,6 @@ #include "qv8sequencewrapper_p.h" #include "qv8include_p.h" #include "qjsengine_p.h" -#include "../../../3rdparty/javascriptcore/DateMath.h" #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmllist_p.h> @@ -765,23 +764,19 @@ void QV8Engine::setExtensionData(int index, Deletable *data) double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt) { - // from QScriptEngine::DateTimeToMs() if (!dt.isValid()) { return qSNaN(); } - QDateTime utc = dt.toUTC(); - QDate date = utc.date(); - QTime time = utc.time(); - QV8DateConverter::JSC::GregorianDateTime tm; - tm.year = date.year() - 1900; - tm.month = date.month() - 1; - tm.monthDay = date.day(); - tm.weekDay = date.dayOfWeek(); - tm.yearDay = date.dayOfYear(); - tm.hour = time.hour(); - tm.minute = time.minute(); - tm.second = time.second(); - return QV8DateConverter::JSC::gregorianDateTimeToMS(tm, time.msec()); + + return dt.toMSecsSinceEpoch(); +} + +QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate) +{ + if (qIsNaN(jsDate)) + return QDateTime(); + + return QDateTime::fromMSecsSinceEpoch(jsDate); } v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong) @@ -815,24 +810,6 @@ v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, boo } } -QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate) -{ - // from QScriptEngine::MsToDateTime() - if (qIsNaN(jsDate)) - return QDateTime(); - QV8DateConverter::JSC::GregorianDateTime tm; - QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm); - - // from QScriptEngine::MsFromTime() - int ms = int(::fmod(jsDate, 1000.0)); - if (ms < 0) - ms += int(1000.0); - - QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), - QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); - return convertedUTC.toLocalTime(); -} - void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle) { if (handle.IsEmpty()) diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp index 2350b9dc2c..ce85725642 100644 --- a/src/qml/qml/v8/qv8qobjectwrapper.cpp +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -63,7 +63,7 @@ Q_DECLARE_METATYPE(QQmlV8Handle); QT_BEGIN_NAMESPACE -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // The code in this file does not violate strict aliasing, but GCC thinks it does // so turn off the warnings for us to have a clean build @@ -269,7 +269,7 @@ static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8: QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); QObject *object = resource->object; - if (!object) return v8::Undefined(); + if (QQmlData::wasDeleted(object)) return v8::Undefined(); QQmlPropertyData *property = (QQmlPropertyData *)v8::External::Unwrap(info.Data()); @@ -476,6 +476,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject objectHandle?*objectHandle:engine->newQObject(object), v8::Integer::New(index) }; + Q_ASSERT(argv[0]->IsObject()); return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv); } static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object, @@ -486,10 +487,14 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject v8::Integer::New(index), v8::Context::GetCallingQmlGlobal() }; + Q_ASSERT(argv[0]->IsObject()); return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv); } }; + if (QQmlData::wasDeleted(object)) + return v8::Handle<v8::Value>(); + { // Comparing the hash first actually makes a measurable difference here, at least on x86 quint32 hash = property.hash(); @@ -705,6 +710,9 @@ bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QH engine->qobjectWrapper()->m_destroyString == property) return true; + if (QQmlData::wasDeleted(object)) + return false; + QQmlPropertyData local; QQmlPropertyData *result = 0; result = QQmlPropertyCache::property(engine->engine(), object, property, local); @@ -735,7 +743,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, { QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); - if (resource->object.isNull()) + if (QQmlData::wasDeleted(resource->object)) return v8::Handle<v8::Value>(); QObject *object = resource->object; @@ -779,7 +787,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, { QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); - if (resource->object.isNull()) + if (QQmlData::wasDeleted(resource->object)) return value; QObject *object = resource->object; @@ -873,7 +881,7 @@ static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, { QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); - if (resource->object.isNull()) + if (QQmlData::wasDeleted(resource->object)) return; QObject *object = resource->object; @@ -900,7 +908,7 @@ static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8 { QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); - if (resource->object.isNull()) + if (QQmlData::wasDeleted(resource->object)) return; QV8Engine *v8engine = resource->engine; @@ -1080,20 +1088,13 @@ released the handle. */ v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) { - if (!object) + if (QQmlData::wasDeleted(object)) return v8::Null(); - if (QObjectPrivate::get(object)->wasDeleted) - return v8::Null(); - QQmlData *ddata = QQmlData::get(object, true); - if (!ddata) return v8::Undefined(); - if (ddata->isQueuedForDeletion) - return v8::Null(); - if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) { // We own the v8object return v8::Local<v8::Object>::New(ddata->v8object); diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri index de492a8ce5..7816c84b79 100644 --- a/src/qml/qml/v8/v8.pri +++ b/src/qml/qml/v8/v8.pri @@ -1,5 +1,3 @@ -INCLUDEPATH += $$PWD/../../../3rdparty/javascriptcore - include(script.pri) HEADERS += \ @@ -20,7 +18,6 @@ HEADERS += \ $$PWD/qv8include_p.h \ $$PWD/qv8worker_p.h \ $$PWD/qv8bindings_p.h \ - $$PWD/../../../3rdparty/javascriptcore/DateMath.h \ $$PWD/qv8engine_impl_p.h \ $$PWD/qv8domerrors_p.h \ $$PWD/qv8sqlerrors_p.h \ @@ -39,7 +36,6 @@ SOURCES += \ $$PWD/qv8include.cpp \ $$PWD/qv8worker.cpp \ $$PWD/qv8bindings.cpp \ - $$PWD/../../../3rdparty/javascriptcore/DateMath.cpp \ $$PWD/qv8domerrors.cpp \ $$PWD/qv8sqlerrors.cpp \ - $$PWD/qqmlbuiltinfunctions.cpp
\ No newline at end of file + $$PWD/qqmlbuiltinfunctions.cpp diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index 6df36d20cb..157cfe97c0 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -4,7 +4,7 @@ TARGET = QtQuickTest QPRO_PWD = $$PWD CONFIG += module -CONFIG += dll warn_on +CONFIG += dll warn_on declarative_debug MODULE_PRI += ../../modules/qt_qmltest.pri QT += testlib testlib-private qml quick gui @@ -33,4 +33,4 @@ HEADERS += \ $$PWD/qtestoptions_p.h -DEFINES += QT_BUILD_QUICK_TEST_LIB +DEFINES += QT_BUILD_QUICK_TEST_LIB
\ No newline at end of file diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 1c69cee456..3a2103afb6 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -100,10 +100,10 @@ static inline QString stripQuotes(const QString &s) return s; } -template <class View> void handleCompileErrors(const QFileInfo &fi, const View &view) +void handleCompileErrors(const QFileInfo &fi, QQuickView *view) { // Error compiling the test - flag failure in the log and continue. - const QList<QQmlError> errors = view.errors(); + const QList<QQmlError> errors = view->errors(); QuickTestResult results; results.setTestCaseName(fi.baseName()); results.startLogging(); @@ -125,8 +125,8 @@ template <class View> void handleCompileErrors(const QFileInfo &fi, const View & str << ": " << e.description() << '\n'; } str << " Working directory: " << QDir::toNativeSeparators(QDir::current().absolutePath()) << '\n'; - if (QQmlEngine *engine = view.engine()) { - str << " View: " << view.metaObject()->className() << ", import paths:\n"; + if (QQmlEngine *engine = view->engine()) { + str << " View: " << view->metaObject()->className() << ", import paths:\n"; foreach (const QString &i, engine->importPathList()) str << " '" << QDir::toNativeSeparators(i) << "'\n"; const QStringList pluginPaths = engine->pluginPathList(); @@ -249,17 +249,17 @@ int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport // Scan through all of the "tst_*.qml" files and run each of them // in turn with a QQuickView. - QQuickView view; + QQuickView *view = new QQuickView; QTestRootObject rootobj; QEventLoop eventLoop; - QObject::connect(view.engine(), SIGNAL(quit()), + QObject::connect(view->engine(), SIGNAL(quit()), &rootobj, SLOT(quit())); - QObject::connect(view.engine(), SIGNAL(quit()), + QObject::connect(view->engine(), SIGNAL(quit()), &eventLoop, SLOT(quit())); - view.rootContext()->setContextProperty + view->rootContext()->setContextProperty (QLatin1String("qtest"), &rootobj); foreach (const QString &path, imports) - view.engine()->addImportPath(path); + view->engine()->addImportPath(path); foreach (QString file, files) { QFileInfo fi(file); @@ -271,13 +271,13 @@ int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport rootobj.hasQuit = false; QString path = fi.absoluteFilePath(); if (path.startsWith(QLatin1String(":/"))) - view.setSource(QUrl(QLatin1String("qrc:") + path.mid(2))); + view->setSource(QUrl(QLatin1String("qrc:") + path.mid(2))); else - view.setSource(QUrl::fromLocalFile(path)); + view->setSource(QUrl::fromLocalFile(path)); if (QTest::printAvailableFunctions) continue; - if (view.status() == QQuickView::Error) { + if (view->status() == QQuickView::Error) { handleCompileErrors(fi, view); continue; } @@ -286,8 +286,8 @@ int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport // synchronously during setSource(). Otherwise it is // an asynchronous test and we need to show the window // and wait for the quit indication. - view.show(); - QTest::qWaitForWindowShown(&view); + view->show(); + QTest::qWaitForWindowShown(view); rootobj.setWindowShown(true); if (!rootobj.hasQuit && rootobj.hasTestCase()) eventLoop.exec(); @@ -296,10 +296,8 @@ int quick_test_main(int argc, char **argv, const char *name, quick_test_viewport // Flush the current logging stream. QuickTestResult::setProgramName(0); - - //Sometimes delete app cause crash here with some qpa plugins, - //so we comment the follow line out to make them happy. - //delete app; + delete view; + delete app; // Return the number of failures as the exit code. return QuickTestResult::exitCode(); diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index a605b9ce6d..6d8d9b852b 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -47,7 +47,6 @@ #include <QtQuick/private/qquickshadereffectsource_p.h> #include <QtGui/qopenglframebufferobject.h> -#include <QtCore/qdebug.h> #include <QtQuick/private/qsgcontext_p.h> #include <private/qquicksvgparser_p.h> #include <private/qquickpath_p.h> @@ -185,7 +184,7 @@ QColor qt_color_from_string(v8::Local<v8::Value> name) if (*p != ')') return QColor(); if (isRgb) return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255))); - else + else if (isHsl) return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)); } return QColor(); @@ -484,8 +483,6 @@ static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args) CHECK_CONTEXT(r) r->context->reset(); - r->context->m_path = QPainterPath(); - r->context->m_path.setFillRule(Qt::WindingFill); return args.This(); } @@ -551,15 +548,8 @@ static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - if (args.Length() == 1) { - qreal angle = args[0]->NumberValue(); - if (!qIsFinite(angle)) - return args.This(); - - r->context->state.matrix.rotate(DEGREES(angle)); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - + if (args.Length() == 1) + r->context->rotate(args[0]->NumberValue()); return args.This(); } @@ -583,17 +573,8 @@ static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args) CHECK_CONTEXT(r) - if (args.Length() == 2) { - qreal x, y; - x = args[0]->NumberValue(); - y = args[1]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->state.matrix.scale(x, y); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - + if (args.Length() == 2) + r->context->scale(args[0]->NumberValue(), args[1]->NumberValue()); return args.This(); } @@ -636,25 +617,13 @@ static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args) CHECK_CONTEXT(r) - if (args.Length() == 6) { - qreal a = args[0]->NumberValue(); - qreal b = args[1]->NumberValue(); - qreal c = args[2]->NumberValue(); - qreal d = args[3]->NumberValue(); - qreal e = args[4]->NumberValue(); - qreal f = args[5]->NumberValue(); - - if (!qIsFinite(a) - || !qIsFinite(b) - || !qIsFinite(c) - || !qIsFinite(d) - || !qIsFinite(e) - || !qIsFinite(f)) - return args.This(); - - r->context->state.matrix = QTransform(a, b, c, d, e, f); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } + if (args.Length() == 6) + r->context->setTransform( args[0]->NumberValue() + , args[1]->NumberValue() + , args[2]->NumberValue() + , args[3]->NumberValue() + , args[4]->NumberValue() + , args[5]->NumberValue()); return args.This(); } @@ -675,25 +644,13 @@ static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args) CHECK_CONTEXT(r) - if (args.Length() == 6) { - qreal a = args[0]->NumberValue(); - qreal b = args[1]->NumberValue(); - qreal c = args[2]->NumberValue(); - qreal d = args[3]->NumberValue(); - qreal e = args[4]->NumberValue(); - qreal f = args[5]->NumberValue(); - - if (!qIsFinite(a) - || !qIsFinite(b) - || !qIsFinite(c) - || !qIsFinite(d) - || !qIsFinite(e) - || !qIsFinite(f)) - return args.This(); - - r->context->state.matrix *= QTransform(a, b, c, d, e, f); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } + if (args.Length() == 6) + r->context->transform( args[0]->NumberValue() + , args[1]->NumberValue() + , args[2]->NumberValue() + , args[3]->NumberValue() + , args[4]->NumberValue() + , args[5]->NumberValue()); return args.This(); } @@ -713,17 +670,8 @@ static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args) CHECK_CONTEXT(r) - if (args.Length() == 2) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - - r->context->state.matrix.translate(x, y); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } - + if (args.Length() == 2) + r->context->translate(args[0]->NumberValue(), args[1]->NumberValue()); return args.This(); } @@ -739,8 +687,7 @@ static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - r->context->state.matrix = QTransform(); - r->context->buffer()->updateMatrix(r->context->state.matrix); + r->context->setTransform(1, 0, 0, 1, 0, 0); return args.This(); } @@ -755,16 +702,9 @@ static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - if (args.Length() == 2) { - qreal sh = args[0]->NumberValue(); - qreal sv = args[1]->NumberValue(); + if (args.Length() == 2) + r->context->shear(args[0]->NumberValue(), args[1]->NumberValue()); - if (!qIsFinite(sh) || !qIsFinite(sv)) - return args.This(); - - r->context->state.matrix.shear(sh, sv); - r->context->buffer()->updateMatrix(r->context->state.matrix); - } return args.This(); } // compositing @@ -1596,17 +1536,11 @@ static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args) CHECK_CONTEXT(r) - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->buffer()->clearRect(x, y, w, h); - } + if (args.Length() == 4) + r->context->clearRect(args[0]->NumberValue(), + args[1]->NumberValue(), + args[2]->NumberValue(), + args[3]->NumberValue()); return args.This(); } @@ -1621,18 +1555,8 @@ static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->buffer()->fillRect(x, y, w, h); - } - + if (args.Length() == 4) + r->context->fillRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue()); return args.This(); } @@ -1651,18 +1575,8 @@ static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->buffer()->strokeRect(x, y, w, h); - } + if (args.Length() == 4) + r->context->strokeRect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue()); return args.This(); } @@ -1687,15 +1601,8 @@ static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args) antiClockwise = args[5]->BooleanValue(); qreal radius = args[2]->NumberValue(); - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal sa = args[3]->NumberValue(); - qreal ea = args[4]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(sa) || !qIsFinite(ea)) - return args.This(); - if (radius < 0) + if (qIsFinite(radius) && radius < 0) V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius"); r->context->arc(args[0]->NumberValue(), @@ -1734,25 +1641,17 @@ static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - - if (args.Length() == 5) { - qreal x1 = args[0]->NumberValue(); - qreal y1 = args[1]->NumberValue(); - qreal x2 = args[2]->NumberValue(); - qreal y2 = args[3]->NumberValue(); - - if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2)) - return args.This(); - qreal radius = args[4]->NumberValue(); - if (radius < 0) + + if (qIsFinite(radius) && radius < 0) V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius"); + r->context->arcTo(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue(), - args[4]->NumberValue()); + radius); } return args.This(); @@ -1845,14 +1744,7 @@ static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - QPainterPath clipPath = r->context->m_path; - clipPath.closeSubpath(); - if (!r->context->state.clipPath.isEmpty()) - r->context->state.clipPath = clipPath.intersected(r->context->state.clipPath); - else - r->context->state.clipPath = clipPath; - r->context->buffer()->clip(r->context->state.clipPath); - + r->context->clip(); return args.This(); } @@ -1887,9 +1779,7 @@ static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args) { QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r); - - r->context->buffer()->fill(r->context->m_path); - + r->context->fill(); return args.This(); } @@ -1975,19 +1865,8 @@ static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - r->context->rect(x, y, w, h); - } - + if (args.Length() == 4) + r->context->rect(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue()); return args.This(); } @@ -2002,23 +1881,13 @@ static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - if (args.Length() == 6) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - qreal xr = args[4]->NumberValue(); - qreal yr = args[5]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - if (!qIsFinite(xr) || !qIsFinite(yr)) - V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "roundedRect(): Invalid arguments"); - - r->context->roundedRect(x, y, w, h, xr, yr); - } - + if (args.Length() == 6) + r->context->roundedRect(args[0]->NumberValue() + , args[1]->NumberValue() + , args[2]->NumberValue() + , args[3]->NumberValue() + , args[4]->NumberValue() + , args[5]->NumberValue()); return args.This(); } @@ -2036,18 +1905,8 @@ static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args) CHECK_CONTEXT(r) - if (args.Length() == 4) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - qreal w = args[2]->NumberValue(); - qreal h = args[3]->NumberValue(); - - if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) - return args.This(); - - - r->context->ellipse(x, y, w, h); - } + if (args.Length() == 4) + r->context->ellipse(args[0]->NumberValue(), args[1]->NumberValue(), args[2]->NumberValue(), args[3]->NumberValue()); return args.This(); } @@ -2089,9 +1948,7 @@ static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args) QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This()); CHECK_CONTEXT(r) - - r->context->buffer()->stroke(r->context->m_path); - + r->context->stroke(); return args.This(); } @@ -2108,13 +1965,8 @@ static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args) CHECK_CONTEXT(r) bool pointInPath = false; - if (args.Length() == 2) { - qreal x = args[0]->NumberValue(); - qreal y = args[1]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return v8::Boolean::New(false); - pointInPath = r->context->isPointInPath(x, y); - } + if (args.Length() == 2) + pointInPath = r->context->isPointInPath(args[0]->NumberValue(), args[1]->NumberValue()); return v8::Boolean::New(pointInPath); } @@ -2331,14 +2183,8 @@ static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args) CHECK_CONTEXT(r) QV8Engine *engine = V8ENGINE(); - if (args.Length() == 3) { - qreal x = args[1]->NumberValue(); - qreal y = args[2]->NumberValue(); - if (!qIsFinite(x) || !qIsFinite(y)) - return args.This(); - QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0])); - r->context->buffer()->stroke(textPath); - } + if (args.Length() == 3) + r->context->drawText(engine->toString(args[0]), args[1]->NumberValue(), args[2]->NumberValue(), false); return args.This(); } /*! @@ -2450,6 +2296,10 @@ static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args) if (!args.Length()) return args.This(); + //FIXME:This function should be moved to QQuickContext2D::drawImage(...) + if (!r->context->state.invertibleCTM) + return args.This(); + QImage image; if (args[0]->IsString()) { QUrl url(engine->toString(args[0]->ToString())); @@ -2868,16 +2718,221 @@ static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &ar return args.This(); } +void QQuickContext2D::scale(qreal x, qreal y) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y)) + return; + + QTransform newTransform = state.matrix; + newTransform.scale(x, y); + + if (!newTransform.isInvertible()) { + state.invertibleCTM = false; + return; + } + + state.matrix = newTransform; + buffer()->updateMatrix(state.matrix); + m_path = QTransform().scale(1.0 / x, 1.0 / y).map(m_path); +} + +void QQuickContext2D::rotate(qreal angle) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(angle)) + return; + + QTransform newTransform =state.matrix; + newTransform.rotate(DEGREES(angle)); + + if (!newTransform.isInvertible()) { + state.invertibleCTM = false; + return; + } + + state.matrix = newTransform; + buffer()->updateMatrix(state.matrix); + m_path = QTransform().rotate(-DEGREES(angle)).map(m_path); +} + +void QQuickContext2D::shear(qreal h, qreal v) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(h) || !qIsFinite(v)) + return ; + + QTransform newTransform = state.matrix; + newTransform.shear(h, v); + + if (!newTransform.isInvertible()) { + state.invertibleCTM = false; + return; + } + + state.matrix = newTransform; + buffer()->updateMatrix(state.matrix); + m_path = QTransform().shear(-h, -v).map(m_path); +} + +void QQuickContext2D::translate(qreal x, qreal y) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y)) + return ; + + QTransform newTransform = state.matrix; + newTransform.translate(x, y); + + if (!newTransform.isInvertible()) { + state.invertibleCTM = false; + return; + } + + state.matrix = newTransform; + buffer()->updateMatrix(state.matrix); + m_path = QTransform().translate(-x, -y).map(m_path); +} + +void QQuickContext2D::transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f)) + return; + + QTransform transform(a, b, c, d, e, f); + QTransform newTransform = state.matrix * transform; + + if (!newTransform.isInvertible()) { + state.invertibleCTM = false; + return; + } + state.matrix = newTransform; + buffer()->updateMatrix(state.matrix); + m_path = transform.inverted().map(m_path); +} + +void QQuickContext2D::setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f) +{ + if (!qIsFinite(a) || !qIsFinite(b) || !qIsFinite(c) || !qIsFinite(d) || !qIsFinite(e) || !qIsFinite(f)) + return; + + QTransform ctm = state.matrix; + if (!ctm.isInvertible()) + return; + + state.matrix = ctm.inverted() * state.matrix; + m_path = ctm.map(m_path); + state.invertibleCTM = true; + transform(a, b, c, d, e, f); +} + +void QQuickContext2D::fill() +{ + if (!state.invertibleCTM) + return; + + if (!m_path.elementCount()) + return; + + m_path.setFillRule(state.fillRule); + buffer()->fill(m_path); +} + +void QQuickContext2D::clip() +{ + if (!state.invertibleCTM) + return; + + QPainterPath clipPath = m_path; + clipPath.closeSubpath(); + if (!state.clipPath.isEmpty()) + state.clipPath = clipPath.intersected(state.clipPath); + else + state.clipPath = clipPath; + buffer()->clip(state.clipPath); +} + +void QQuickContext2D::stroke() +{ + if (!state.invertibleCTM) + return; + + if (!m_path.elementCount()) + return; + + buffer()->stroke(m_path); +} + +void QQuickContext2D::fillRect(qreal x, qreal y, qreal w, qreal h) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return; + + buffer()->fillRect(x, y, w, h); +} + +void QQuickContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return; + + buffer()->strokeRect(x, y, w, h); +} + +void QQuickContext2D::clearRect(qreal x, qreal y, qreal w, qreal h) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return; + + buffer()->clearRect(x, y, w, h); +} + +void QQuickContext2D::drawText(const QString& text, qreal x, qreal y, bool fill) +{ + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y)) + return; + + QPainterPath textPath = createTextGlyphs(x, y, text); + if (fill) + buffer()->fill(textPath); + else + buffer()->stroke(textPath); +} + void QQuickContext2D::beginPath() { + if (!m_path.elementCount()) + return; m_path = QPainterPath(); - m_path.setFillRule(state.fillRule); } void QQuickContext2D::closePath() { - if (m_path.isEmpty()) + if (!m_path.elementCount()) return; QRectF boundRect = m_path.boundingRect(); @@ -2889,29 +2944,53 @@ void QQuickContext2D::closePath() void QQuickContext2D::moveTo( qreal x, qreal y) { + if (!state.invertibleCTM) + return; + //FIXME: moveTo should not close the previous subpath - m_path.moveTo(state.matrix.map(QPointF(x, y))); + m_path.moveTo(QPointF(x, y)); } void QQuickContext2D::lineTo( qreal x, qreal y) { - m_path.lineTo(state.matrix.map(QPointF(x, y))); + if (!state.invertibleCTM) + return; + + QPointF pt(x, y); + + if (!m_path.elementCount()) + m_path.moveTo(pt); + else if (m_path.currentPosition() != pt) + m_path.lineTo(pt); } void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y) { - m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)), - state.matrix.map(QPointF(x, y))); + if (!state.invertibleCTM) + return; + + if (!m_path.elementCount()) + m_path.moveTo(QPointF(cpx, cpy)); + + QPointF pt(x, y); + if (m_path.currentPosition() != pt) + m_path.quadTo(QPointF(cpx, cpy), pt); } void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y, qreal cp2x, qreal cp2y, qreal x, qreal y) { - m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)), - state.matrix.map(QPointF(cp2x, cp2y)), - state.matrix.map(QPointF(x, y))); + if (!state.invertibleCTM) + return; + + if (!m_path.elementCount()) + m_path.moveTo(QPointF(cp1x, cp1y)); + + QPointF pt(x, y); + if (m_path.currentPosition() != pt) + m_path.cubicTo(QPointF(cp1x, cp1y), QPointF(cp2x, cp2y), pt); } void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius) @@ -2969,69 +3048,100 @@ void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radiu if ((sa < ea) && ((ea - sa) > Q_PI)) anticlockwise = true; - arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false); + arc(p.x(), p.y(), radius, sa, ea, anticlockwise); } void QQuickContext2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius) { - QPointF st = state.matrix.map(QPointF(x1, y1)); - QPointF end = state.matrix.map(QPointF(x2, y2)); + if (!state.invertibleCTM) + return; - if (!m_path.elementCount()) { + if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2) || !qIsFinite(radius)) + return; + + QPointF st(x1, y1); + QPointF end(x2, y2); + + if (!m_path.elementCount()) m_path.moveTo(st); - } else if (st == m_path.currentPosition() || st == end || !radius) { - m_path.lineTo(st); - } else { + else if (st == m_path.currentPosition() || st == end || !radius) + lineTo(x1, y1); + else addArcTo(st, end, radius); - } -} + } -void QQuickContext2D::rect(qreal x, qreal y, - qreal w, qreal h) +void QQuickContext2D::rect(qreal x, qreal y, qreal w, qreal h) { - m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h))); + if (!state.invertibleCTM) + return; + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return; + + if (!w && !h) { + m_path.moveTo(x, y); + return; + } + m_path.addRect(x, y, w, h); } void QQuickContext2D::roundedRect(qreal x, qreal y, qreal w, qreal h, qreal xr, qreal yr) { - QPainterPath path; - path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize); - m_path.addPath(state.matrix.map(path)); + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h) || !qIsFinite(xr) || !qIsFinite(yr)) + return; + + if (!w && !h) { + m_path.moveTo(x, y); + return; + } + m_path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize); } void QQuickContext2D::ellipse(qreal x, qreal y, qreal w, qreal h) { - QPainterPath path; - path.addEllipse(x, y, w, h); - m_path.addPath(state.matrix.map(path)); + if (!state.invertibleCTM) + return; + + if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h)) + return; + + if (!w && !h) { + m_path.moveTo(x, y); + return; + } + + m_path.addEllipse(x, y, w, h); } void QQuickContext2D::text(const QString& str, qreal x, qreal y) { + if (!state.invertibleCTM) + return; + QPainterPath path; path.addText(x, y, state.font, str); - m_path.addPath(state.matrix.map(path)); + m_path.addPath(path); } -void QQuickContext2D::arc(qreal xc, - qreal yc, - qreal radius, - qreal sar, - qreal ear, - bool antiClockWise, - bool transform) +void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear, bool antiClockWise) { + if (!state.invertibleCTM) + return; + + if (!qIsFinite(xc) || !qIsFinite(yc) || !qIsFinite(sar) || !qIsFinite(ear) || !qIsFinite(radius)) + return; + + if (sar == ear) + return; + - if (transform) { - QPointF point = state.matrix.map(QPointF(xc, yc)); - xc = point.x(); - yc = point.y(); - } //### HACK // In Qt we don't switch the coordinate system for degrees @@ -3068,17 +3178,14 @@ void QQuickContext2D::arc(qreal xc, qFuzzyCompare(qAbs(span), 360))) { span += ea - sa; } - if (!m_path.elementCount()) - m_path.moveTo(xs, ys); } - - if (transform) { - QPointF currentPos = m_path.currentPosition(); - QPointF startPos = QPointF(xc + radius * qCos(sar), - yc - radius * qSin(sar)); - if (currentPos != startPos) - m_path.lineTo(startPos); + // If the path is empty, move to where the arc will start to avoid painting a line from (0,0) + if (!m_path.elementCount()) + m_path.arcMoveTo(xs, ys, width, height, sa); + else if (!radius) { + m_path.lineTo(xc, yc); + return; } m_path.arcTo(xs, ys, width, height, sa, span); @@ -3142,9 +3249,59 @@ QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& } +static inline bool areCollinear(const QPointF& a, const QPointF& b, const QPointF& c) +{ + // Solved from comparing the slopes of a to b and b to c: (ay-by)/(ax-bx) == (cy-by)/(cx-bx) + return qFuzzyCompare((c.y() - b.y()) * (a.x() - b.x()), (a.y() - b.y()) * (c.x() - b.x())); +} + +static inline bool withinRange(qreal p, qreal a, qreal b) +{ + return (p >= a && p <= b) || (p >= b && p <= a); +} + bool QQuickContext2D::isPointInPath(qreal x, qreal y) const { - return m_path.contains(QPointF(x, y)); + if (!state.invertibleCTM) + return false; + + if (!m_path.elementCount()) + return false; + + if (!qIsFinite(x) || !qIsFinite(y)) + return false; + + QPointF point(x, y); + QTransform ctm = state.matrix; + QPointF p = ctm.inverted().map(point); + if (!qIsFinite(p.x()) || !qIsFinite(p.y())) + return false; + + const_cast<QQuickContext2D *>(this)->m_path.setFillRule(state.fillRule); + + bool contains = m_path.contains(p); + + if (!contains) { + // check whether the point is on the border + QPolygonF border = m_path.toFillPolygon(); + + QPointF p1 = border.at(0); + QPointF p2; + + for (int i = 1; i < border.size(); ++i) { + p2 = border.at(i); + if (areCollinear(p, p1, p2) + // Once we know that the points are collinear we + // only need to check one of the coordinates + && (qAbs(p2.x() - p1.x()) > qAbs(p2.y() - p1.y()) ? + withinRange(p.x(), p1.x(), p2.x()) : + withinRange(p.y(), p1.y(), p2.y()))) { + return true; + } + p1 = p2; + } + } + return contains; } QQuickContext2D::QQuickContext2D(QObject *parent) @@ -3405,7 +3562,9 @@ void QQuickContext2D::popState() if (newState.shadowOffsetY != state.shadowOffsetY) buffer()->setShadowOffsetY(newState.shadowOffsetY); + m_path = state.matrix.map(m_path); state = newState; + m_path = state.matrix.inverted().map(m_path); } void QQuickContext2D::pushState() { @@ -3417,6 +3576,8 @@ void QQuickContext2D::reset() QQuickContext2D::State newState; newState.matrix = QTransform(); + m_path = QPainterPath(); + QPainterPath defaultClipPath; QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height()); @@ -3431,6 +3592,7 @@ void QQuickContext2D::reset() newState.fillPatternRepeatY = false; newState.strokePatternRepeatX = false; newState.strokePatternRepeatY = false; + newState.invertibleCTM = true; newState.fillRule = Qt::WindingFill; newState.globalAlpha = 1.0; newState.lineWidth = 1; diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 3230881134..4112d4ebf0 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -116,6 +116,7 @@ public: , fillPatternRepeatY(false) , strokePatternRepeatX(false) , strokePatternRepeatY(false) + , invertibleCTM(true) , fillRule(Qt::WindingFill) , globalAlpha(1.0) , lineWidth(1) @@ -141,6 +142,7 @@ public: bool fillPatternRepeatY:1; bool strokePatternRepeatX:1; bool strokePatternRepeatY:1; + bool invertibleCTM:1; Qt::FillRule fillRule; qreal globalAlpha; qreal lineWidth; @@ -180,7 +182,23 @@ public: void pushState(); void reset(); - // path API + void fill(); + void clip(); + void stroke(); + void fillRect(qreal x, qreal y, qreal w, qreal h); + void strokeRect(qreal x, qreal y, qreal w, qreal h); + void clearRect(qreal x, qreal y, qreal w, qreal h); + void drawText(const QString& text, qreal x, qreal y, bool fill); + + //Transform APIs + void scale(qreal x, qreal y); + void rotate(qreal angle); + void shear(qreal h, qreal v); + void translate(qreal x, qreal y); + void transform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f); + void setTransform(qreal a, qreal b, qreal c, qreal d, qreal e, qreal f); + + // Path APIs void beginPath(); void closePath(); void moveTo(qreal x, qreal y); @@ -195,7 +213,7 @@ public: void text(const QString& str, qreal x, qreal y); void arc(qreal x, qreal y, qreal radius, qreal startAngle, qreal endAngle, - bool anticlockwise, bool transform=true); + bool anticlockwise); void addArcTo(const QPointF& p1, const QPointF& p2, float radius); bool isPointInPath(qreal x, qreal y) const; diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index 591fc216a4..f6b9a1afeb 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -236,7 +236,7 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s reset(); - QTransform originMatrix = p->transform(); + QTransform originMatrix = p->worldTransform(); QPen pen = makePen(state); setPainterState(p, state, pen); @@ -247,7 +247,7 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s case QQuickContext2D::UpdateMatrix: { state.matrix = takeMatrix(); - p->setTransform(state.matrix * originMatrix); + p->setWorldTransform(state.matrix * originMatrix); break; } case QQuickContext2D::ClearRect: @@ -303,36 +303,42 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s state.strokeStyle = takeStrokeStyle(); state.strokePatternRepeatX = takeBool(); state.strokePatternRepeatY = takeBool(); - pen.setBrush(state.strokeStyle); - p->setPen(pen); + QPen nPen = p->pen(); + nPen.setBrush(state.strokeStyle); + p->setPen(nPen); break; } case QQuickContext2D::LineWidth: { state.lineWidth = takeLineWidth(); - pen.setWidth(state.lineWidth); - p->setPen(pen); + QPen nPen = p->pen(); + + nPen.setWidthF(state.lineWidth); + p->setPen(nPen); break; } case QQuickContext2D::LineCap: { state.lineCap = takeLineCap(); - pen.setCapStyle(state.lineCap); - p->setPen(pen); + QPen nPen = p->pen(); + nPen.setCapStyle(state.lineCap); + p->setPen(nPen); break; } case QQuickContext2D::LineJoin: { state.lineJoin = takeLineJoin(); - pen.setJoinStyle(state.lineJoin); - p->setPen(pen); + QPen nPen = p->pen(); + nPen.setJoinStyle(state.lineJoin); + p->setPen(nPen); break; } case QQuickContext2D::MiterLimit: { state.miterLimit = takeMiterLimit(); - pen.setMiterLimit(state.miterLimit); - p->setPen(pen); + QPen nPen = p->pen(); + nPen.setMiterLimit(state.miterLimit); + p->setPen(nPen); break; } case QQuickContext2D::TextAlign: diff --git a/src/quick/items/qquickcanvas.cpp b/src/quick/items/qquickcanvas.cpp index 2e2c8725aa..3f08c8f056 100644 --- a/src/quick/items/qquickcanvas.cpp +++ b/src/quick/items/qquickcanvas.cpp @@ -55,7 +55,6 @@ #include <private/qguiapplication_p.h> #include <QtGui/QInputMethod> -#include <QtGui/QCursor> #include <private/qabstractanimation_p.h> @@ -123,10 +122,12 @@ private: bool m_eventSent; }; +#ifndef QT_NO_ACCESSIBILITY QAccessibleInterface *QQuickCanvas::accessibleRoot() const { return QAccessible::queryAccessibleInterface(const_cast<QQuickCanvas*>(this)); } +#endif /* @@ -154,6 +155,10 @@ have a scope focused item), and the other items will have their focus cleared. // #define TOUCH_DEBUG // #define DIRTY_DEBUG +#ifdef FOCUS_DEBUG +void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1); +#endif + QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() : transformNode(0) { @@ -952,7 +957,7 @@ bool QQuickCanvasPrivate::clearHover() if (hoverItems.isEmpty()) return false; - QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos()); + QPointF pos = QGuiApplicationPrivate::lastCursorPosition;; // ### refactor: q->mapFromGlobal(QCursor::pos()); bool accepted = false; foreach (QQuickItem* item, hoverItems) @@ -1087,8 +1092,12 @@ bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event) QQuickMouseEventEx me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(), event->button(), event->buttons(), event->modifiers()); - if (QQuickMouseEventEx::extended(event)) - me.setVelocity(QQuickMouseEventEx::extended(event)->velocity()); + QQuickMouseEventEx *eventEx = QQuickMouseEventEx::extended(event); + if (eventEx) { + me.setVelocity(eventEx->velocity()); + me.setCapabilities(eventEx->capabilities()); + } + me.setTimestamp(event->timestamp()); me.accept(); q->sendEvent(mouseGrabberItem, &me); event->setAccepted(me.isAccepted()); diff --git a/src/quick/items/qquickcanvas.h b/src/quick/items/qquickcanvas.h index 787bb7e3c7..396bc2b8ff 100644 --- a/src/quick/items/qquickcanvas.h +++ b/src/quick/items/qquickcanvas.h @@ -102,7 +102,9 @@ public: QQmlIncubationController *incubationController() const; +#ifndef QT_NO_ACCESSIBILITY virtual QAccessibleInterface *accessibleRoot() const; +#endif // Scene graph specific functions QSGTexture *createTextureFromImage(const QImage &image) const; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 62d0e4aa41..75c9919e34 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1108,7 +1108,8 @@ void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event) d->clearDelayedPress(); d->handleMouseReleaseEvent(event); event->accept(); - ungrabMouse(); + if (canvas() && canvas()->mouseGrabberItem() == this) + ungrabMouse(); } else { QQuickItem::mouseReleaseEvent(event); } diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp index 427be42312..1de8e0ab73 100644 --- a/src/quick/items/qquickimplicitsizeitem.cpp +++ b/src/quick/items/qquickimplicitsizeitem.cpp @@ -47,12 +47,24 @@ QT_BEGIN_NAMESPACE void QQuickImplicitSizeItemPrivate::implicitWidthChanged() { Q_Q(QQuickImplicitSizeItem); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::ImplicitWidth) { + change.listener->itemImplicitWidthChanged(q); + } + } emit q->implicitWidthChanged(); } void QQuickImplicitSizeItemPrivate::implicitHeightChanged() { Q_Q(QQuickImplicitSizeItem); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::ImplicitHeight) { + change.listener->itemImplicitHeightChanged(q); + } + } emit q->implicitHeightChanged(); } diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 1cf10df2f7..ffaec540a2 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -52,8 +52,8 @@ #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlinfo.h> #include <QtGui/qpen.h> -#include <QtGui/qcursor.h> #include <QtGui/qguiapplication.h> +#include <QtGui/private/qguiapplication_p.h> #include <QtGui/qinputmethod.h> #include <QtCore/qdebug.h> #include <QtCore/qcoreevent.h> @@ -75,6 +75,26 @@ QT_BEGIN_NAMESPACE +#ifdef FOCUS_DEBUG +void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1); +void printFocusTree(QQuickItem *item, QQuickItem *scope, int depth) +{ + qWarning() + << QByteArray(depth, '\t').constData() + << (scope && QQuickItemPrivate::get(scope)->subFocusItem == item ? '*' : ' ') + << item->hasFocus() + << item->hasActiveFocus() + << item->isFocusScope() + << item; + foreach (QQuickItem *child, item->childItems()) { + printFocusTree( + child, + item->isFocusScope() || !scope ? item : scope, + item->isFocusScope() || !scope ? depth + 1 : depth); + } +} +#endif + static void QQuickItem_parentNotifier(QObject *o, intptr_t, QQmlNotifier **n) { QQuickItemPrivate *d = QQuickItemPrivate::get(static_cast<QQuickItem *>(o)); @@ -1613,9 +1633,6 @@ void QQuickItemPrivate::setAccessibleFlagAndListener() if (item->d_func()->isAccessible) break; // already set - grandparents should have the flag set as well. - if (item->canvas() && item->canvas()->rootItem() == item) - break; // don't add a listener to the canvas root item - item->d_func()->isAccessible = true; item = item->d_func()->parentItem; } @@ -1939,6 +1956,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) if (d->canvas) { QQuickCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem, QQuickCanvasPrivate::DontChangeFocusProperty); + if (scopeFocusedItem != this) + QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true); } else { QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, false); } @@ -1990,7 +2009,10 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) while (!scopeItem->isFocusScope() && scopeItem->parentItem()) scopeItem = scopeItem->parentItem(); - if (scopeItem->scopedFocusItem()) { + if (QQuickItemPrivate::get(scopeItem)->subFocusItem + || (!scopeItem->isFocusScope() && scopeItem->hasFocus())) { + if (scopeFocusedItem != this) + QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, false); QQuickItemPrivate::get(scopeFocusedItem)->focus = false; emit scopeFocusedItem->focusChanged(false); } else { @@ -4145,7 +4167,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec for (int ii = 0; ii < childItems.count(); ++ii) { QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur( - flags & QQuickItem::ItemIsFocusScope ? q : scope, newEffectiveEnable); + (flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable); } if (canvas && scope && effectiveEnable && focus) { @@ -4509,6 +4531,12 @@ void QQuickItem::resetWidth() void QQuickItemPrivate::implicitWidthChanged() { Q_Q(QQuickItem); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::ImplicitWidth) { + change.listener->itemImplicitWidthChanged(q); + } + } emit q->implicitWidthChanged(); } @@ -4631,6 +4659,12 @@ void QQuickItem::resetHeight() void QQuickItemPrivate::implicitHeightChanged() { Q_Q(QQuickItem); + for (int ii = 0; ii < changeListeners.count(); ++ii) { + const QQuickItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QQuickItemPrivate::ImplicitHeight) { + change.listener->itemImplicitHeightChanged(q); + } + } emit q->implicitHeightChanged(); } @@ -4781,6 +4815,7 @@ void QQuickItem::setFocus(bool focus) QVarLengthArray<QQuickItem *, 20> changed; QQuickItem *oldSubFocusItem = QQuickItemPrivate::get(scope)->subFocusItem; if (oldSubFocusItem) { + QQuickItemPrivate::get(oldSubFocusItem)->updateSubFocusItem(scope, false); QQuickItemPrivate::get(oldSubFocusItem)->focus = false; changed << oldSubFocusItem; } @@ -4850,7 +4885,7 @@ bool QQuickItem::isUnderMouse() const if (!d->canvas) return false; - QPoint cursorPos = QCursor::pos(); + QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition; if (QRectF(0, 0, width(), height()).contains(mapFromScene(cursorPos))) // ### refactor: d->canvas->mapFromGlobal(cursorPos)))) return true; return false; @@ -5669,10 +5704,11 @@ void QQuickItemLayer::activateEffect() Q_ASSERT(m_effectComponent); Q_ASSERT(!m_effect); - QObject *created = m_effectComponent->create(); + QObject *created = m_effectComponent->beginCreate(m_effectComponent->creationContext()); m_effect = qobject_cast<QQuickItem *>(created); if (!m_effect) { qWarning("Item: layer.effect is not a QML Item."); + m_effectComponent->completeCreate(); delete created; return; } @@ -5683,6 +5719,7 @@ void QQuickItemLayer::activateEffect() } m_effect->setVisible(m_item->isVisible()); m_effect->setProperty(m_name, qVariantFromValue<QObject *>(m_effectSource)); + m_effectComponent->completeCreate(); } void QQuickItemLayer::deactivateEffect() diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 01e8b4d335..89c09ed015 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -302,6 +302,8 @@ public: Parent = 0x20, Children = 0x40, Rotation = 0x80, + ImplicitWidth = 0x100, + ImplicitHeight = 0x200 }; Q_DECLARE_FLAGS(ChangeTypes, ChangeType) diff --git a/src/quick/items/qquickitemchangelistener_p.h b/src/quick/items/qquickitemchangelistener_p.h index 3a5c25f5f5..cbdfb2b18b 100644 --- a/src/quick/items/qquickitemchangelistener_p.h +++ b/src/quick/items/qquickitemchangelistener_p.h @@ -74,6 +74,8 @@ public: virtual void itemChildRemoved(QQuickItem *, QQuickItem *) {} virtual void itemParentChanged(QQuickItem *, QQuickItem *) {} virtual void itemRotationChanged(QQuickItem *) {} + virtual void itemImplicitWidthChanged(QQuickItem *) {} + virtual void itemImplicitHeightChanged(QQuickItem *) {} virtual QQuickAnchorsPrivate *anchorPrivate() { return 0; } }; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 6f33545185..1cc2637fe6 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -577,7 +577,7 @@ void QQuickListViewPrivate::initializeViewItem(FxViewItem *item) itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); if (sectionCriteria && sectionCriteria->delegate()) { - if (item->attached->m_prevSection != item->attached->m_section) + if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive)) updateInlineSection(static_cast<FxListItemSG*>(item)); } } @@ -962,7 +962,7 @@ void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem) { if (!sectionCriteria || !sectionCriteria->delegate()) return; - if (listItem->attached->m_prevSection != listItem->attached->m_section + if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive) && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) { if (!listItem->section()) { @@ -1022,7 +1022,7 @@ void QQuickListViewPrivate::updateStickySections() if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) { if (!currentSectionItem) { currentSectionItem = getSectionItem(currentSection); - } else if (currentStickySection != currentSection) { + } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) { QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext(); context->setContextProperty(QLatin1String("section"), currentSection); } @@ -1055,7 +1055,7 @@ void QQuickListViewPrivate::updateStickySections() if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) { if (!nextSectionItem) { nextSectionItem = getSectionItem(nextSection); - } else if (nextStickySection != nextSection) { + } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) { QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext(); context->setContextProperty(QLatin1String("section"), nextSection); } @@ -2040,6 +2040,9 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation) sections, etc. for an address book) \endlist + A case insensitive comparison is used when determining section + boundaries. + \c section.delegate holds the delegate component for each section. \c section.labelPositioning determines whether the current and/or diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index f41ba44943..864e03bf43 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -52,9 +52,11 @@ QT_BEGIN_NAMESPACE +static const QQuickItemPrivate::ChangeTypes watchedChanges + = QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight; + QQuickLoaderPrivate::QQuickLoaderPrivate() : item(0), component(0), itemContext(0), incubator(0), updatingSize(false), - itemWidthValid(false), itemHeightValid(false), active(true), loadingFromSource(false), asynchronous(false) { } @@ -69,16 +71,23 @@ QQuickLoaderPrivate::~QQuickLoaderPrivate() void QQuickLoaderPrivate::itemGeometryChanged(QQuickItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) { - if (resizeItem == item) { - if (!updatingSize && newGeometry.width() != oldGeometry.width()) - itemWidthValid = true; - if (!updatingSize && newGeometry.height() != oldGeometry.height()) - itemHeightValid = true; + if (resizeItem == item) _q_updateSize(false); - } QQuickItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); } +void QQuickLoaderPrivate::itemImplicitWidthChanged(QQuickItem *) +{ + Q_Q(QQuickLoader); + q->setImplicitWidth(getImplicitWidth()); +} + +void QQuickLoaderPrivate::itemImplicitHeightChanged(QQuickItem *) +{ + Q_Q(QQuickLoader); + q->setImplicitHeight(getImplicitHeight()); +} + void QQuickLoaderPrivate::clear() { Q_Q(QQuickLoader); @@ -103,7 +112,7 @@ void QQuickLoaderPrivate::clear() if (item) { QQuickItemPrivate *p = QQuickItemPrivate::get(item); - p->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + p->removeItemChangeListener(this, watchedChanges); // We can't delete immediately because our item may have triggered // the Loader to load a different item. @@ -117,14 +126,32 @@ void QQuickLoaderPrivate::clear() void QQuickLoaderPrivate::initResize() { QQuickItemPrivate *p = QQuickItemPrivate::get(item); - p->addItemChangeListener(this, QQuickItemPrivate::Geometry); - // We may override the item's size, so we need to remember - // whether the item provided its own valid size. - itemWidthValid = p->widthValid; - itemHeightValid = p->heightValid; + p->addItemChangeListener(this, watchedChanges); _q_updateSize(); } +qreal QQuickLoaderPrivate::getImplicitWidth() const +{ + Q_Q(const QQuickLoader); + // If the Loader has a valid width then Loader has set an explicit width on the + // item, and we want the item's implicitWidth. If the Loader's width has + // not been set then its implicitWidth is the width of the item. + if (item) + return q->widthValid() ? item->implicitWidth() : item->width(); + return QQuickImplicitSizeItemPrivate::getImplicitWidth(); +} + +qreal QQuickLoaderPrivate::getImplicitHeight() const +{ + Q_Q(const QQuickLoader); + // If the Loader has a valid height then Loader has set an explicit height on the + // item, and we want the item's implicitHeight. If the Loader's height has + // not been set then its implicitHeight is the height of the item. + if (item) + return q->heightValid() ? item->implicitHeight() : item->height(); + return QQuickImplicitSizeItemPrivate::getImplicitHeight(); +} + /*! \qmlclass Loader QQuickLoader \inqmlmodule QtQuick 2 @@ -245,7 +272,7 @@ QQuickLoader::~QQuickLoader() Q_D(QQuickLoader); if (d->item) { QQuickItemPrivate *p = QQuickItemPrivate::get(d->item); - p->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + p->removeItemChangeListener(d, watchedChanges); } } @@ -282,9 +309,16 @@ void QQuickLoader::setActive(bool newVal) loadFromSourceComponent(); } } else { + // cancel any current incubation + if (d->incubator) { + d->incubator->clear(); + delete d->itemContext; + d->itemContext = 0; + } + if (d->item) { QQuickItemPrivate *p = QQuickItemPrivate::get(d->item); - p->removeItemChangeListener(d, QQuickItemPrivate::Geometry); + p->removeItemChangeListener(d, watchedChanges); // We can't delete immediately because our item may have triggered // the Loader to load a different item. @@ -553,6 +587,14 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj) QQuickItem *item = qobject_cast<QQuickItem*>(obj); if (item) { + // If the item doesn't have an explicit size, but the Loader + // does, then set the item's size now before bindings are + // evaluated, otherwise we will end up resizing the item + // later and triggering any affected bindings/anchors. + if (widthValid && !QQuickItemPrivate::get(item)->widthValid) + item->setWidth(q->width()); + if (heightValid && !QQuickItemPrivate::get(item)->heightValid) + item->setHeight(q->height()); QQml_setParent_noEvent(itemContext, obj); QQml_setParent_noEvent(item, q); item->setParentItem(q); @@ -607,7 +649,8 @@ void QQuickLoaderPrivate::incubatorStateChanged(QQmlIncubator::Status status) emit q->sourceComponentChanged(); emit q->statusChanged(); emit q->progressChanged(); - emit q->loaded(); + if (status == QQmlIncubator::Ready) + emit q->loaded(); disposeInitialPropertyValues(); // cleanup } @@ -812,15 +855,13 @@ void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) updatingSize = true; - qreal iWidth = !itemWidthValid ? item->implicitWidth() : item->width(); - qreal iHeight = !itemHeightValid ? item->implicitHeight() : item->height(); - q->setImplicitSize(iWidth, iHeight); - if (loaderGeometryChanged && q->widthValid()) item->setWidth(q->width()); if (loaderGeometryChanged && q->heightValid()) item->setHeight(q->height()); + q->setImplicitSize(getImplicitWidth(), getImplicitHeight()); + updatingSize = false; } diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 1ad7756ed8..0978911315 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -87,6 +87,8 @@ public: ~QQuickLoaderPrivate(); void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void itemImplicitWidthChanged(QQuickItem *); + void itemImplicitHeightChanged(QQuickItem *); void clear(); void initResize(); void load(); @@ -97,6 +99,9 @@ public: QUrl resolveSourceUrl(QQmlV8Function *args); v8::Handle<v8::Object> extractInitialPropertyValues(QQmlV8Function *args, QObject *loader, bool *error); + virtual qreal getImplicitWidth() const; + virtual qreal getImplicitHeight() const; + QUrl source; QQuickItem *item; QQmlComponent *component; @@ -105,8 +110,6 @@ public: v8::Persistent<v8::Object> initialPropertyValues; v8::Persistent<v8::Object> qmlGlobalForIpv; bool updatingSize: 1; - bool itemWidthValid : 1; - bool itemHeightValid : 1; bool active : 1; bool loadingFromSource : 1; bool asynchronous : 1; diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp index 110cc6ad7a..2ba7b80748 100644 --- a/src/quick/items/qquickmultipointtoucharea.cpp +++ b/src/quick/items/qquickmultipointtoucharea.cpp @@ -236,7 +236,7 @@ void QQuickTouchPoint::setSceneY(qreal sceneY) \list \li setting \c touchPoints to provide touch point objects with properties that can be bound to - \li using the onTouchUpdated or onTouchPointsPressed, onTouchPointsUpdated and onTouchPointsReleased handlers + \li using the onTouchUpdated or onPressed, onUpdated and onReleased handlers \endlist While a MultiPointTouchArea \e can take exclusive ownership of certain touch points, it is also possible to have diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 962dddafc9..836943c478 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -58,13 +58,13 @@ // The number of samples to use in calculating the velocity of a flick #ifndef QML_FLICK_SAMPLEBUFFER -#define QML_FLICK_SAMPLEBUFFER 3 +#define QML_FLICK_SAMPLEBUFFER 1 #endif // The number of samples to discard when calculating the flick velocity. // Touch panels often produce inaccurate results as the finger is lifted. #ifndef QML_FLICK_DISCARDSAMPLES -#define QML_FLICK_DISCARDSAMPLES 1 +#define QML_FLICK_DISCARDSAMPLES 0 #endif // The default maximum velocity of a flick. @@ -114,8 +114,8 @@ void QQuickPathViewAttached::setValue(const QByteArray &name, const QVariant &va } QQuickPathViewPrivate::QQuickPathViewPrivate() - : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0) - , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0) + : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0) + , offset(0.0), offsetAdj(0.0), mappedRange(1.0) , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) , autoHighlight(true), highlightUp(false), layoutScheduled(false) , moving(false), flicking(false), requestedOnPath(false), inRequest(false) @@ -127,7 +127,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate() , highlightPosition(0) , highlightRangeStart(0), highlightRangeEnd(0) , highlightRangeMode(QQuickPathView::StrictlyEnforceRange) - , highlightMoveDuration(300), modelCount(0) + , highlightMoveDuration(300), modelCount(0), snapMode(QQuickPathView::NoSnap) { } @@ -139,7 +139,7 @@ void QQuickPathViewPrivate::init() q->setFlag(QQuickItem::ItemIsFocusScope); q->setFiltersChildMouseEvents(true); FAST_CONNECT(&tl, SIGNAL(updated()), q, SLOT(ticked())) - lastPosTime.invalidate(); + timer.invalidate(); FAST_CONNECT(&tl, SIGNAL(completed()), q, SLOT(movementEnding())) } @@ -266,7 +266,8 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const if (model && index >= 0 && index < modelCount) { qreal start = 0.0; - if (haveHighlightRange && highlightRangeMode != QQuickPathView::NoHighlightRange) + if (haveHighlightRange && (highlightRangeMode != QQuickPathView::NoHighlightRange + || snapMode != QQuickPathView::NoSnap)) start = highlightRangeStart; qreal globalPos = index + offset; globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount; @@ -698,7 +699,7 @@ void QQuickPathView::setCurrentIndex(int idx) if (d->modelCount) { d->createCurrentItem(); if (d->haveHighlightRange && d->highlightRangeMode == QQuickPathView::StrictlyEnforceRange) - d->snapToCurrent(); + d->snapToIndex(d->currentIndex); d->currentItemOffset = d->positionOfIndex(d->currentIndex); d->updateHighlight(); } @@ -889,7 +890,7 @@ void QQuickPathView::setPreferredHighlightBegin(qreal start) if (d->highlightRangeStart == start || start < 0 || start > 1.0) return; d->highlightRangeStart = start; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd; refill(); emit preferredHighlightBeginChanged(); } @@ -906,7 +907,7 @@ void QQuickPathView::setPreferredHighlightEnd(qreal end) if (d->highlightRangeEnd == end || end < 0 || end > 1.0) return; d->highlightRangeEnd = end; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd; refill(); emit preferredHighlightEndChanged(); } @@ -923,10 +924,12 @@ void QQuickPathView::setHighlightRangeMode(HighlightRangeMode mode) if (d->highlightRangeMode == mode) return; d->highlightRangeMode = mode; - d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + d->haveHighlightRange = d->highlightRangeStart <= d->highlightRangeEnd; if (d->haveHighlightRange) { d->regenerate(); - d->snapToCurrent(); + int index = d->highlightRangeMode != NoHighlightRange ? d->currentIndex : d->calcCurrentIndex(); + if (index >= 0) + d->snapToIndex(index); } emit highlightRangeModeChanged(); } @@ -1175,6 +1178,41 @@ void QQuickPathView::setPathItemCount(int i) emit pathItemCountChanged(); } +/*! + \qmlproperty enumeration QtQuick2::PathView::snapMode + + This property determines how the items will settle following a drag or flick. + The possible values are: + + \list + \li PathView.NoSnap (default) - the items stop anywhere along the path. + \li PathView.SnapToItem - the items settle with an item aligned with the \l preferredHighlightBegin. + \li PathView.SnapOneItem - the items settle no more than one item away from the item nearest + \l preferredHighlightBegin at the time the press is released. This mode is particularly + useful for moving one page at a time. + \endlist + + \c snapMode does not affect the \l currentIndex. To update the + \l currentIndex as the view is moved, set \l highlightRangeMode + to \c PathView.StrictlyEnforceRange (default for PathView). + + \sa highlightRangeMode +*/ +QQuickPathView::SnapMode QQuickPathView::snapMode() const +{ + Q_D(const QQuickPathView); + return d->snapMode; +} + +void QQuickPathView::setSnapMode(SnapMode mode) +{ + Q_D(QQuickPathView); + if (mode == d->snapMode) + return; + d->snapMode = mode; + emit snapModeChanged(); +} + QPointF QQuickPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const { qreal samples = qMin(path->path().length()/5, qreal(500.0)); @@ -1236,6 +1274,15 @@ qreal QQuickPathViewPrivate::calcVelocity() const return velocity; } +qint64 QQuickPathViewPrivate::computeCurrentTime(QInputEvent *event) +{ + if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1) { + return event->timestamp(); + } + + return QQuickItemPrivate::elapsed(timer); +} + void QQuickPathView::mousePressEvent(QMouseEvent *event) { Q_D(QQuickPathView); @@ -1277,9 +1324,8 @@ void QQuickPathViewPrivate::handleMousePressEvent(QMouseEvent *event) else stealMouse = false; - lastElapsed = 0; - lastDist = 0; - QQuickItemPrivate::start(lastPosTime); + QQuickItemPrivate::start(timer); + lastPosTime = computeCurrentTime(event); tl.clear(); } @@ -1299,7 +1345,7 @@ void QQuickPathView::mouseMoveEvent(QMouseEvent *event) void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event) { Q_Q(QQuickPathView); - if (!interactive || !lastPosTime.isValid() || !model || !modelCount) + if (!interactive || !timer.isValid() || !model || !modelCount) return; qreal newPc; @@ -1308,10 +1354,10 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event) QPointF delta = pathPoint - startPoint; if (qAbs(delta.x()) > qApp->styleHints()->startDragDistance() || qAbs(delta.y()) > qApp->styleHints()->startDragDistance()) { stealMouse = true; - startPc = newPc; } } + qint64 currentTimestamp = computeCurrentTime(event); if (stealMouse) { moveReason = QQuickPathViewPrivate::Mouse; qreal diff = (newPc - startPc)*modelCount*mappedRange; @@ -1323,10 +1369,9 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event) else if (diff < -modelCount/2) diff += modelCount; - lastElapsed = QQuickItemPrivate::restart(lastPosTime); - lastDist = diff; - startPc = newPc; - addVelocitySample(diff / (qreal(lastElapsed) / 1000.)); + qint64 elapsed = currentTimestamp - lastPosTime; + if (elapsed > 0) + addVelocitySample(diff / (qreal(elapsed) / 1000.)); } if (!moving) { moving = true; @@ -1334,6 +1379,8 @@ void QQuickPathViewPrivate::handleMouseMoveEvent(QMouseEvent *event) emit q->movementStarted(); } } + startPc = newPc; + lastPosTime = currentTimestamp; } void QQuickPathView::mouseReleaseEvent(QMouseEvent *event) @@ -1353,8 +1400,8 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) Q_Q(QQuickPathView); stealMouse = false; q->setKeepMouseGrab(false); - if (!interactive || !lastPosTime.isValid() || !model || !modelCount) { - lastPosTime.invalidate(); + if (!interactive || !timer.isValid() || !model || !modelCount) { + timer.invalidate(); if (!tl.isActive()) q->movementEnding(); return; @@ -1364,7 +1411,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) qreal count = modelCount*mappedRange; qreal pixelVelocity = (path->path().length()/count) * velocity; if (qAbs(pixelVelocity) > MinimumFlickVelocity) { - if (qAbs(pixelVelocity) > maximumFlickVelocity) { + if (qAbs(pixelVelocity) > maximumFlickVelocity || snapMode == QQuickPathView::SnapOneItem) { // limit velocity qreal maxVel = velocity < 0 ? -maximumFlickVelocity : maximumFlickVelocity; velocity = maxVel / (path->path().length()/count); @@ -1373,14 +1420,24 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) qreal v2 = velocity*velocity; qreal accel = deceleration/10; qreal dist = 0; - if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { - // + 0.25 to encourage moving at least one item in the flick direction - dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25)); - // round to nearest item. - if (velocity > 0.) - dist = qRound(dist + offset) - offset; - else - dist = qRound(dist - offset) + offset; + if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange + || snapMode != QQuickPathView::NoSnap)) { + if (snapMode == QQuickPathView::SnapOneItem) { + // encourage snapping one item in direction of motion + if (velocity > 0.) + dist = qRound(0.5 + offset) - offset; + else + dist = qRound(0.5 - offset) + offset; + } else { + // + 0.25 to encourage moving at least one item in the flick direction + dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25)); + + // round to nearest item. + if (velocity > 0.) + dist = qRound(dist + offset) - offset; + else + dist = qRound(dist - offset) + offset; + } // Calculate accel required to stop on item boundary if (dist <= 0.) { dist = 0.; @@ -1405,7 +1462,7 @@ void QQuickPathViewPrivate::handleMouseReleaseEvent(QMouseEvent *) fixOffset(); } - lastPosTime.invalidate(); + timer.invalidate(); if (!tl.isActive()) q->movementEnding(); } @@ -1441,8 +1498,8 @@ bool QQuickPathView::sendMouseEvent(QMouseEvent *event) grabMouse(); return d->stealMouse; - } else if (d->lastPosTime.isValid()) { - d->lastPosTime.invalidate(); + } else if (d->timer.isValid()) { + d->timer.invalidate(); d->fixOffset(); } if (event->type() == QEvent::MouseButtonRelease) @@ -1476,7 +1533,7 @@ void QQuickPathView::mouseUngrabEvent() // fix our state d->stealMouse = false; setKeepMouseGrab(false); - d->lastPosTime.invalidate(); + d->timer.invalidate(); d->fixOffset(); if (!d->tl.isActive()) movementEnding(); @@ -1557,7 +1614,8 @@ void QQuickPathView::refill() if (d->items.count() < count) { int idx = qRound(d->modelCount - d->offset) % d->modelCount; qreal startPos = 0.0; - if (d->haveHighlightRange && d->highlightRangeMode != QQuickPathView::NoHighlightRange) + if (d->haveHighlightRange && (d->highlightRangeMode != QQuickPathView::NoHighlightRange + || d->snapMode != QQuickPathView::NoSnap)) startPos = d->highlightRangeStart; if (d->firstIndex >= 0) { startPos = d->positionOfIndex(d->firstIndex); @@ -1833,22 +1891,23 @@ void QQuickPathViewPrivate::fixOffset() { Q_Q(QQuickPathView); if (model && items.count()) { - if (haveHighlightRange && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) { + if (haveHighlightRange && (highlightRangeMode == QQuickPathView::StrictlyEnforceRange + || snapMode != QQuickPathView::NoSnap)) { int curr = calcCurrentIndex(); - if (curr != currentIndex) + if (curr != currentIndex && highlightRangeMode == QQuickPathView::StrictlyEnforceRange) q->setCurrentIndex(curr); else - snapToCurrent(); + snapToIndex(curr); } } } -void QQuickPathViewPrivate::snapToCurrent() +void QQuickPathViewPrivate::snapToIndex(int index) { if (!model || modelCount <= 0) return; - qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount); + qreal targetOffset = qmlMod(modelCount - index, modelCount); if (offset == targetOffset) return; diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h index 8b15e85b8a..2c0c106113 100644 --- a/src/quick/items/qquickpathview_p.h +++ b/src/quick/items/qquickpathview_p.h @@ -83,8 +83,10 @@ class Q_AUTOTEST_EXPORT QQuickPathView : public QQuickItem Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged) + Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged) Q_ENUMS(HighlightRangeMode) + Q_ENUMS(SnapMode) public: QQuickPathView(QQuickItem *parent=0); @@ -144,6 +146,10 @@ public: int pathItemCount() const; void setPathItemCount(int); + enum SnapMode { NoSnap, SnapToItem, SnapOneItem }; + SnapMode snapMode() const; + void setSnapMode(SnapMode mode); + static QQuickPathViewAttached *qmlAttachedProperties(QObject *); public Q_SLOTS: @@ -176,6 +182,7 @@ Q_SIGNALS: void movementEnded(); void flickStarted(); void flickEnded(); + void snapModeChanged(); protected: virtual void updatePolish(); diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index ac74e9a568..3285b40036 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -122,10 +122,11 @@ public: void setAdjustedOffset(qreal offset); void regenerate(); void updateItem(QQuickItem *, qreal); - void snapToCurrent(); + void snapToIndex(int index); QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; void addVelocitySample(qreal v); qreal calcVelocity() const; + qint64 computeCurrentTime(QInputEvent *event); QQuickPath *path; int currentIndex; @@ -133,8 +134,6 @@ public: qreal currentItemOffset; qreal startPc; QPointF startPoint; - qreal lastDist; - int lastElapsed; qreal offset; qreal offsetAdj; qreal mappedRange; @@ -149,7 +148,8 @@ public: bool flicking : 1; bool requestedOnPath : 1; bool inRequest : 1; - QElapsedTimer lastPosTime; + QElapsedTimer timer; + qint64 lastPosTime; QPointF lastPos; qreal dragMargin; qreal deceleration; @@ -180,6 +180,7 @@ public: int highlightMoveDuration; int modelCount; QPODVector<qreal,10> velocityBuffer; + QQuickPathView::SnapMode snapMode; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index e232746417..1549057ef8 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -88,6 +88,446 @@ const char *qtTexCoordAttributeName() return qt_texcoord_attribute_name; } +namespace { + + enum VariableQualifier { + AttributeQualifier, + UniformQualifier + }; + + inline bool qt_isalpha(char c) + { + char ch = c | 0x20; + return (ch >= 'a' && ch <= 'z') || c == '_'; + } + + inline bool qt_isalnum(char c) + { + return qt_isalpha(c) || (c >= '0' && c <= '9'); + } + + inline bool qt_isspace(char c) + { + return c == ' ' || (c >= 0x09 && c <= 0x0d); + } + + // Returns -1 if not found, returns index to first character after the name if found. + int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl, + int &typeIndex, int &typeLength, + int &nameIndex, int &nameLength) + { + enum Identifier { + QualifierIdentifier, // Base state + PrecisionIdentifier, + TypeIdentifier, + NameIdentifier + }; + Identifier expected = QualifierIdentifier; + bool compilerDirectiveExpected = index == 0; + + while (index < length) { + // Skip whitespace. + while (qt_isspace(s[index])) { + compilerDirectiveExpected |= s[index] == '\n'; + ++index; + } + + if (qt_isalpha(s[index])) { + // Read identifier. + int idIndex = index; + ++index; + while (qt_isalnum(s[index])) + ++index; + int idLength = index - idIndex; + + const int attrLen = sizeof("attribute") - 1; + const int uniLen = sizeof("uniform") - 1; + const int loLen = sizeof("lowp") - 1; + const int medLen = sizeof("mediump") - 1; + const int hiLen = sizeof("highp") - 1; + + switch (expected) { + case QualifierIdentifier: + if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) { + decl = AttributeQualifier; + expected = PrecisionIdentifier; + } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) { + decl = UniformQualifier; + expected = PrecisionIdentifier; + } + break; + case PrecisionIdentifier: + if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0) + || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0) + || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0)) + { + expected = TypeIdentifier; + break; + } + // Fall through. + case TypeIdentifier: + typeIndex = idIndex; + typeLength = idLength; + expected = NameIdentifier; + break; + case NameIdentifier: + nameIndex = idIndex; + nameLength = idLength; + return index; // Attribute or uniform declaration found. Return result. + default: + break; + } + } else if (s[index] == '#' && compilerDirectiveExpected) { + // Skip compiler directives. + ++index; + while (index < length && (s[index] != '\n' || s[index - 1] == '\\')) + ++index; + } else if (s[index] == '/' && s[index + 1] == '/') { + // Skip comments. + index += 2; + while (index < length && s[index] != '\n') + ++index; + } else if (s[index] == '/' && s[index + 1] == '*') { + // Skip comments. + index += 2; + while (index < length && (s[index] != '*' || s[index + 1] != '/')) + ++index; + if (index < length) + index += 2; // Skip star-slash. + } else { + expected = QualifierIdentifier; + ++index; + } + compilerDirectiveExpected = false; + } + return -1; + } +} + + + +QQuickShaderEffectCommon::~QQuickShaderEffectCommon() +{ + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) + qDeleteAll(signalMappers[shaderType]); +} + +void QQuickShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) +{ + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + if (signalMappers[shaderType].at(i) == 0) + continue; + const UniformData &d = uniformData[shaderType].at(i); + QSignalMapper *mapper = signalMappers[shaderType].at(i); + QObject::disconnect(item, 0, mapper, SLOT(map())); + QObject::disconnect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int))); + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source) { + if (item->canvas()) + QQuickItemPrivate::get(source)->derefCanvas(); + QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + } + } +} + +void QQuickShaderEffectCommon::connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) +{ + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + if (signalMappers[shaderType].at(i) == 0) + continue; + const UniformData &d = uniformData[shaderType].at(i); + int pi = item->metaObject()->indexOfProperty(d.name.constData()); + if (pi >= 0) { + QMetaProperty mp = item->metaObject()->property(pi); + if (!mp.hasNotifySignal()) + qWarning("QQuickShaderEffect: property '%s' does not have notification method!", d.name.constData()); + QByteArray signalName("2"); + signalName.append(mp.notifySignal().methodSignature()); + QSignalMapper *mapper = signalMappers[shaderType].at(i); + QObject::connect(item, signalName, mapper, SLOT(map())); + QObject::connect(mapper, SIGNAL(mapped(int)), item, SLOT(propertyChanged(int))); + } else { + // If the source is set via a dynamic property, like the layer is, then we need this + // check to disable the warning. + if (!item->property(d.name.constData()).isValid()) + qWarning("QQuickShaderEffect: '%s' does not have a matching property!", d.name.constData()); + } + + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source) { + if (item->canvas()) + QQuickItemPrivate::get(source)->refCanvas(item->canvas()); + QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + } + } +} + +void QQuickShaderEffectCommon::updateParseLog(bool ignoreAttributes) +{ + parseLog.clear(); + if (!ignoreAttributes) { + if (!attributes.contains(qt_position_attribute_name)) { + parseLog += QLatin1String("Warning: Missing reference to \'"); + parseLog += QLatin1String(qt_position_attribute_name); + parseLog += QLatin1String("\'.\n"); + } + if (!attributes.contains(qt_texcoord_attribute_name)) { + parseLog += QLatin1String("Warning: Missing reference to \'"); + parseLog += QLatin1String(qt_texcoord_attribute_name); + parseLog += QLatin1String("\'.\n"); + } + } + bool respectsMatrix = false; + bool respectsOpacity = false; + for (int i = 0; i < uniformData[Key::VertexShader].size(); ++i) + respectsMatrix |= uniformData[Key::VertexShader].at(i).specialType == UniformData::Matrix; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) + respectsOpacity |= uniformData[shaderType].at(i).specialType == UniformData::Opacity; + } + if (!respectsMatrix) + parseLog += QLatin1String("Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n"); + if (!respectsOpacity) + parseLog += QLatin1String("Warning: Shaders are missing reference to \'qt_Opacity\'.\n"); +} + +void QQuickShaderEffectCommon::lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code) +{ + int index = 0; + int typeIndex, typeLength, nameIndex, nameLength; + const char *s = code.constData(); + VariableQualifier decl; + while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength, + nameIndex, nameLength)) != -1) + { + if (decl == AttributeQualifier) { + if (shaderType == Key::VertexShader) + attributes.append(QByteArray(s + nameIndex, nameLength)); + } else { + Q_ASSERT(decl == UniformQualifier); + + const int sampLen = sizeof("sampler2D") - 1; + const int opLen = sizeof("qt_Opacity") - 1; + const int matLen = sizeof("qt_Matrix") - 1; + + UniformData d; + QSignalMapper *mapper = 0; + d.name = QByteArray(s + nameIndex, nameLength); + if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) { + d.specialType = UniformData::Opacity; + } else if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) { + d.specialType = UniformData::Matrix; + } else { + mapper = new QSignalMapper; + mapper->setMapping(item, uniformData[shaderType].size() | (shaderType << 16)); + d.value = item->property(d.name.constData()); + bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0; + d.specialType = sampler ? UniformData::Sampler : UniformData::None; + } + uniformData[shaderType].append(d); + signalMappers[shaderType].append(mapper); + } + } +} + +void QQuickShaderEffectCommon::updateShader(QQuickItem *item, Key::ShaderType shaderType) +{ + disconnectPropertySignals(item, shaderType); + qDeleteAll(signalMappers[shaderType]); + uniformData[shaderType].clear(); + signalMappers[shaderType].clear(); + if (shaderType == Key::VertexShader) + attributes.clear(); + + const QByteArray &code = source.sourceCode[shaderType]; + if (code.isEmpty()) { + // Optimize for default code. + if (shaderType == Key::VertexShader) { + attributes.append(QByteArray(qt_position_attribute_name)); + attributes.append(QByteArray(qt_texcoord_attribute_name)); + UniformData d; + d.name = "qt_Matrix"; + d.specialType = UniformData::Matrix; + uniformData[Key::VertexShader].append(d); + signalMappers[Key::VertexShader].append(0); + } else if (shaderType == Key::FragmentShader) { + UniformData d; + d.name = "qt_Opacity"; + d.specialType = UniformData::Opacity; + uniformData[Key::FragmentShader].append(d); + signalMappers[Key::FragmentShader].append(0); + QSignalMapper *mapper = new QSignalMapper; + mapper->setMapping(item, 1 | (Key::FragmentShader << 16)); + const char *sourceName = "source"; + d.name = sourceName; + d.value = item->property(sourceName); + d.specialType = UniformData::Sampler; + uniformData[Key::FragmentShader].append(d); + signalMappers[Key::FragmentShader].append(mapper); + } + } else { + lookThroughShaderCode(item, shaderType, code); + } + + connectPropertySignals(item, shaderType); +} + +void QQuickShaderEffectCommon::updateMaterial(QQuickShaderEffectNode *node, + QQuickShaderEffectMaterial *material, + bool updateUniforms, bool updateUniformValues, + bool updateTextureProviders) +{ + if (updateUniforms) { + for (int i = 0; i < material->textureProviders.size(); ++i) { + QSGTextureProvider *t = material->textureProviders.at(i).second; + if (t) { + QObject::disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + QObject::disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } + } + material->textureProviders.clear(); + + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + // First make room in the textureProviders array. Set to proper value further down. + if (d.specialType == UniformData::Sampler) + material->textureProviders.append(qMakePair(d.name, (QSGTextureProvider *)0)); + } + material->uniforms[shaderType] = uniformData[shaderType]; + } + updateUniformValues = false; + updateTextureProviders = true; + } + + if (updateUniformValues) { + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + Q_ASSERT(uniformData[shaderType].size() == material->uniforms[shaderType].size()); + for (int i = 0; i < uniformData[shaderType].size(); ++i) + material->uniforms[shaderType][i].value = uniformData[shaderType].at(i).value; + } + } + + if (updateTextureProviders) { + int index = 0; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + if (d.specialType != UniformData::Sampler) + continue; + Q_ASSERT(material->textureProviders.at(index).first == d.name); + QSGTextureProvider *oldProvider = material->textureProviders.at(index).second; + QSGTextureProvider *newProvider = 0; + QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source && source->isTextureProvider()) + newProvider = source->textureProvider(); + if (newProvider != oldProvider) { + if (oldProvider) { + QObject::disconnect(oldProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + QObject::disconnect(oldProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } + if (newProvider) { + Q_ASSERT_X(newProvider->thread() == QThread::currentThread(), + "QQuickShaderEffect::updatePaintNode", + "Texture provider must belong to the rendering thread"); + QObject::connect(newProvider, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); + QObject::connect(newProvider, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); + } else { + const char *typeName = source ? source->metaObject()->className() : d.value.typeName(); + qWarning("ShaderEffect: Property '%s' is not assigned a valid texture provider (%s).", + d.name.constData(), typeName); + } + material->textureProviders[index].second = newProvider; + } + ++index; + } + } + Q_ASSERT(index == material->textureProviders.size()); + } +} + +void QQuickShaderEffectCommon::updateCanvas(QQuickCanvas *canvas) +{ + // See comment in QQuickShaderEffectCommon::propertyChanged(). + if (canvas) { + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source) + QQuickItemPrivate::get(source)->refCanvas(canvas); + } + } + } + } else { + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + const UniformData &d = uniformData[shaderType].at(i); + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source) + QQuickItemPrivate::get(source)->derefCanvas(); + } + } + } + } +} + +void QQuickShaderEffectCommon::sourceDestroyed(QObject *object) +{ + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < uniformData[shaderType].size(); ++i) { + UniformData &d = uniformData[shaderType][i]; + if (d.specialType == UniformData::Sampler && qVariantCanConvert<QObject *>(d.value)) { + if (qVariantValue<QObject *>(d.value) == object) + d.value = QVariant(); + } + } + } +} + + +void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, + bool *textureProviderChanged) +{ + Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16); + int index = mappedId & 0xffff; + UniformData &d = uniformData[shaderType][index]; + if (d.specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source) { + if (item->canvas()) + QQuickItemPrivate::get(source)->derefCanvas(); + QObject::disconnect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + + d.value = item->property(d.name.constData()); + + source = qobject_cast<QQuickItem *>(qVariantValue<QObject *>(d.value)); + if (source) { + // 'source' needs a canvas to get a scene graph node. It usually gets one through its + // parent, but if the source item is "inline" rather than a reference -- i.e. + // "property variant source: Image { }" instead of "property variant source: foo" -- it + // will not get a parent. In those cases, 'source' should get the canvas from 'item'. + if (item->canvas()) + QQuickItemPrivate::get(source)->refCanvas(item->canvas()); + QObject::connect(source, SIGNAL(destroyed(QObject*)), item, SLOT(sourceDestroyed(QObject*))); + } + if (textureProviderChanged) + *textureProviderChanged = true; + } else { + d.value = item->property(d.name.constData()); + if (textureProviderChanged) + *textureProviderChanged = false; + } +} + + /*! \qmlclass ShaderEffect QQuickShaderEffect \inqmlmodule QtQuick 2 @@ -187,18 +627,21 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent) , m_cullMode(NoCulling) , m_status(Uncompiled) , m_blending(true) - , m_dirtyData(true) - , m_programDirty(true) + , m_dirtyUniforms(true) + , m_dirtyUniformValues(true) + , m_dirtyTextureProviders(true) + , m_dirtyProgram(true) + , m_dirtyParseLog(true) , m_dirtyMesh(true) , m_dirtyGeometry(true) - , m_complete(false) { setFlag(QQuickItem::ItemHasContents); } QQuickShaderEffect::~QQuickShaderEffect() { - reset(); + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) + m_common.disconnectPropertySignals(this, Key::ShaderType(shaderType)); } /*! @@ -211,11 +654,16 @@ QQuickShaderEffect::~QQuickShaderEffect() void QQuickShaderEffect::setFragmentShader(const QByteArray &code) { - if (m_source.fragmentCode.constData() == code.constData()) + if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData()) return; - m_source.fragmentCode = code; + m_common.source.sourceCode[Key::FragmentShader] = code; + m_dirtyProgram = true; + m_dirtyParseLog = true; + + if (isComponentComplete()) + m_common.updateShader(this, Key::FragmentShader); + update(); - m_complete = false; if (m_status != Uncompiled) { m_status = Uncompiled; emit statusChanged(); @@ -234,11 +682,16 @@ void QQuickShaderEffect::setFragmentShader(const QByteArray &code) void QQuickShaderEffect::setVertexShader(const QByteArray &code) { - if (m_source.vertexCode.constData() == code.constData()) + if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData()) return; - m_source.vertexCode = code; + m_common.source.sourceCode[Key::VertexShader] = code; + m_dirtyProgram = true; + m_dirtyParseLog = true; + + if (isComponentComplete()) + m_common.updateShader(this, Key::VertexShader); + update(); - m_complete = false; if (m_status != Uncompiled) { m_status = Uncompiled; emit statusChanged(); @@ -316,6 +769,7 @@ void QQuickShaderEffect::setMesh(const QVariant &mesh) } m_dirtyMesh = true; + m_dirtyParseLog = true; update(); emit meshChanged(); } @@ -343,6 +797,34 @@ void QQuickShaderEffect::setCullMode(CullMode face) emit cullModeChanged(); } +QString QQuickShaderEffect::parseLog() +{ + if (m_dirtyParseLog) { + m_common.updateParseLog(m_mesh != 0); + m_dirtyParseLog = false; + } + return m_common.parseLog; +} + +bool QQuickShaderEffect::event(QEvent *event) +{ + if (event->type() == QEvent::DynamicPropertyChange) { + QDynamicPropertyChangeEvent *e = static_cast<QDynamicPropertyChangeEvent *>(event); + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) { + if (m_common.uniformData[shaderType].at(i).name == e->propertyName()) { + bool textureProviderChanged; + m_common.propertyChanged(this, (shaderType << 16) | i, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); + } + } + } + } + return QQuickItem::event(event); +} + /*! \qmlproperty enumeration QtQuick2::ShaderEffect::status @@ -371,19 +853,6 @@ void QQuickShaderEffect::setCullMode(CullMode face) \sa status */ -void QQuickShaderEffect::changeSource(int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - QVariant v = property(m_sources.at(index).name.constData()); - setSource(v, index); -} - -void QQuickShaderEffect::updateData() -{ - m_dirtyData = true; - update(); -} - void QQuickShaderEffect::updateGeometry() { m_dirtyGeometry = true; @@ -392,7 +861,7 @@ void QQuickShaderEffect::updateGeometry() void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status) { - m_log = m_parseLog + log; + m_log = parseLog() + log; m_status = Status(status); emit logChanged(); emit statusChanged(); @@ -400,341 +869,17 @@ void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status) void QQuickShaderEffect::sourceDestroyed(QObject *object) { - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - if (object == source.sourceObject) - source.sourceObject = 0; - } -} - -void QQuickShaderEffect::setSource(const QVariant &var, int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - - SourceData &source = m_sources[index]; - - if (source.sourceObject) { - if (canvas()) - QQuickItemPrivate::get(source.sourceObject)->derefCanvas(); - disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); - } - - source.sourceObject = 0; - if (var.isNull()) { - return; - } else if (!qVariantCanConvert<QObject *>(var)) { - qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); - return; - } - - QObject *obj = qVariantValue<QObject *>(var); - if (!obj) - return; - QQuickItem *item = qobject_cast<QQuickItem *>(obj); - if (!item || !item->isTextureProvider()) { - qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]", - source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className()); - return; - } - - source.sourceObject = item; - - if (item) { - // 'item' needs a canvas to get a scene graph node. It usually gets one through its - // parent, but if the source item is "inline" rather than a reference -- i.e. - // "property variant source: Image { }" instead of "property variant source: foo" -- it - // will not get a parent. In those cases, 'item' should get the canvas from 'this'. - if (canvas()) - QQuickItemPrivate::get(item)->refCanvas(canvas()); - connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); - } -} - -void QQuickShaderEffect::disconnectPropertySignals() -{ - disconnect(this, 0, this, SLOT(updateData())); - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - disconnect(this, 0, source.mapper, 0); - disconnect(source.mapper, 0, this, 0); - } -} - -void QQuickShaderEffect::connectPropertySignals() -{ - QSet<QByteArray>::const_iterator it; - for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { - int pi = metaObject()->indexOfProperty(it->constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - if (!mp.hasNotifySignal()) - qWarning("QQuickShaderEffect: property '%s' does not have notification method!", it->constData()); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().methodSignature()); - connect(this, signalName, this, SLOT(updateData())); - } else { - // If the source is set via a dynamic property, like the layer is, then we need this check - // to disable the warning. - if (property(it->constData()).isValid()) - continue; - qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData()); - } - } - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - int pi = metaObject()->indexOfProperty(source.name.constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().methodSignature()); - connect(this, signalName, source.mapper, SLOT(map())); - source.mapper->setMapping(this, i); - connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); - } else { - // If the source is set via a dynamic property, like the layer is, then we need this check - // to disable the warning. - if (property(source.name.constData()).isValid()) - continue; - qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData()); - } - } -} - - -void QQuickShaderEffect::ensureCompleted() -{ - if (!m_complete) { - reset(); - updateProperties(); - m_complete = true; - } -} - - -void QQuickShaderEffect::reset() -{ - disconnectPropertySignals(); - - m_source.attributeNames.clear(); - m_source.uniformNames.clear(); - m_source.respectsOpacity = false; - m_source.respectsMatrix = false; - m_source.className = metaObject()->className(); - - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - delete source.mapper; - if (source.sourceObject) { - if (canvas()) - QQuickItemPrivate::get(source.sourceObject)->derefCanvas(); - disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*))); - } - } - m_sources.clear(); - m_log.clear(); - m_parseLog.clear(); - m_programDirty = true; - m_dirtyMesh = true; -} - -void QQuickShaderEffect::updateProperties() -{ - if (m_source.vertexCode.isEmpty()) { - m_source.attributeNames.append(QByteArray(qt_position_attribute_name)); - m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name)); - m_source.respectsMatrix = true; - } else { - lookThroughShaderCode(m_source.vertexCode); - } - if (m_source.fragmentCode.isEmpty()) { - m_source.respectsOpacity = true; - QByteArray name("source"); - m_source.uniformNames.insert(name); - SourceData d; - d.mapper = new QSignalMapper; - d.name = name; - d.sourceObject = 0; - m_sources.append(d); - } else { - lookThroughShaderCode(m_source.fragmentCode); - } - - if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) { - m_parseLog += QLatin1String("Warning: Missing reference to \'"); - m_parseLog += QLatin1String(qt_position_attribute_name); - m_parseLog += QLatin1String("\'.\n"); - } - if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name)) { - m_parseLog += QLatin1String("Warning: Missing reference to \'"); - m_parseLog += QLatin1String(qt_texcoord_attribute_name); - m_parseLog += QLatin1String("\'.\n"); - } - if (!m_source.respectsMatrix) { - m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Matrix\'.\n"); - } - if (!m_source.respectsOpacity) { - m_parseLog += QLatin1String("Warning: Missing reference to \'qt_Opacity\'.\n"); - } - - for (int i = 0; i < m_sources.size(); ++i) { - QVariant v = property(m_sources.at(i).name); - setSource(v, i); - } - - connectPropertySignals(); + m_common.sourceDestroyed(object); } -namespace { - - enum VariableQualifier { - AttributeQualifier, - UniformQualifier - }; - - inline bool qt_isalpha(char c) - { - char ch = c | 0x20; - return (ch >= 'a' && ch <= 'z') || c == '_'; - } - - inline bool qt_isalnum(char c) - { - return qt_isalpha(c) || (c >= '0' && c <= '9'); - } - - inline bool qt_isspace(char c) - { - return c == ' ' || (c >= 0x09 && c <= 0x0d); - } - - // Returns -1 if not found, returns index to first character after the name if found. - int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl, - int &typeIndex, int &typeLength, - int &nameIndex, int &nameLength) - { - enum Identifier { - QualifierIdentifier, // Base state - PrecisionIdentifier, - TypeIdentifier, - NameIdentifier - }; - Identifier expected = QualifierIdentifier; - bool compilerDirectiveExpected = index == 0; - - while (index < length) { - // Skip whitespace. - while (qt_isspace(s[index])) { - compilerDirectiveExpected |= s[index] == '\n'; - ++index; - } - - if (qt_isalpha(s[index])) { - // Read identifier. - int idIndex = index; - ++index; - while (qt_isalnum(s[index])) - ++index; - int idLength = index - idIndex; - - const int attrLen = sizeof("attribute") - 1; - const int uniLen = sizeof("uniform") - 1; - const int loLen = sizeof("lowp") - 1; - const int medLen = sizeof("mediump") - 1; - const int hiLen = sizeof("highp") - 1; - - switch (expected) { - case QualifierIdentifier: - if (idLength == attrLen && qstrncmp("attribute", s + idIndex, attrLen) == 0) { - decl = AttributeQualifier; - expected = PrecisionIdentifier; - } else if (idLength == uniLen && qstrncmp("uniform", s + idIndex, uniLen) == 0) { - decl = UniformQualifier; - expected = PrecisionIdentifier; - } - break; - case PrecisionIdentifier: - if ((idLength == loLen && qstrncmp("lowp", s + idIndex, loLen) == 0) - || (idLength == medLen && qstrncmp("mediump", s + idIndex, medLen) == 0) - || (idLength == hiLen && qstrncmp("highp", s + idIndex, hiLen) == 0)) - { - expected = TypeIdentifier; - break; - } - // Fall through. - case TypeIdentifier: - typeIndex = idIndex; - typeLength = idLength; - expected = NameIdentifier; - break; - case NameIdentifier: - nameIndex = idIndex; - nameLength = idLength; - return index; // Attribute or uniform declaration found. Return result. - default: - break; - } - } else if (s[index] == '#' && compilerDirectiveExpected) { - // Skip compiler directives. - ++index; - while (index < length && (s[index] != '\n' || s[index - 1] == '\\')) - ++index; - } else if (s[index] == '/' && s[index + 1] == '/') { - // Skip comments. - index += 2; - while (index < length && s[index] != '\n') - ++index; - } else if (s[index] == '/' && s[index + 1] == '*') { - // Skip comments. - index += 2; - while (index < length && (s[index] != '*' || s[index + 1] != '/')) - ++index; - if (index < length) - index += 2; // Skip star-slash. - } else { - expected = QualifierIdentifier; - ++index; - } - compilerDirectiveExpected = false; - } - return -1; - } -} -void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code) +void QQuickShaderEffect::propertyChanged(int mappedId) { - int index = 0; - int typeIndex, typeLength, nameIndex, nameLength; - const char *s = code.constData(); - VariableQualifier decl; - while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength, - nameIndex, nameLength)) != -1) - { - if (decl == AttributeQualifier) { - m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength)); - } else { - Q_ASSERT(decl == UniformQualifier); - - const int matLen = sizeof("qt_Matrix") - 1; - const int opLen = sizeof("qt_Opacity") - 1; - const int sampLen = sizeof("sampler2D") - 1; - - if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) { - m_source.respectsMatrix = true; - } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) { - m_source.respectsOpacity = true; - } else { - QByteArray name(s + nameIndex, nameLength); - m_source.uniformNames.insert(name); - if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) { - SourceData d; - d.mapper = new QSignalMapper; - d.name = name; - d.sourceObject = 0; - m_sources.append(d); - } - } - } - } + bool textureProviderChanged; + m_common.propertyChanged(this, mappedId, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); } void QQuickShaderEffect::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) @@ -747,10 +892,8 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa { QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode); - ensureCompleted(); - // In the case of a bad vertex shader, don't try to create a node... - if (m_source.attributeNames.isEmpty()) { + if (m_common.attributes.isEmpty()) { if (node) delete node; return 0; @@ -758,14 +901,14 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa if (!node) { node = new QQuickShaderEffectNode; - m_programDirty = true; - m_dirtyData = true; + node->setMaterial(new QQuickShaderEffectMaterial(node)); + node->setFlag(QSGNode::OwnsMaterial, true); + m_dirtyProgram = true; + m_dirtyUniforms = true; m_dirtyGeometry = true; connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int))); } - QQuickShaderEffectMaterial *material = node->shaderMaterial(); - if (m_dirtyMesh) { node->setGeometry(0); m_dirtyMesh = false; @@ -778,11 +921,11 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa QRectF rect(0, 0, width(), height()); QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; - geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect); + geometry = mesh->updateGeometry(geometry, m_common.attributes, rect); if (!geometry) { QString log = mesh->log(); if (!log.isNull()) { - m_log = m_parseLog; + m_log = parseLog(); m_log += QLatin1String("*** Mesh ***\n"); m_log += log; m_status = Error; @@ -799,18 +942,7 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa m_dirtyGeometry = false; } - if (m_programDirty) { - QQuickShaderEffectProgram s = m_source; - if (s.fragmentCode.isEmpty()) - s.fragmentCode = qt_default_fragment_code; - if (s.vertexCode.isEmpty()) - s.vertexCode = qt_default_vertex_code; - s.className = metaObject()->className(); - - material->setProgramSource(s); - node->markDirty(QSGNode::DirtyMaterial); - m_programDirty = false; - } + QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(node->material()); // Update blending if (bool(material->flags() & QSGMaterial::Blending) != m_blending) { @@ -818,66 +950,48 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa node->markDirty(QSGNode::DirtyMaterial); } - if (int(material->cullMode()) != int(m_cullMode)) { - material->setCullMode(QQuickShaderEffectMaterial::CullMode(m_cullMode)); + if (int(material->cullMode) != int(m_cullMode)) { + material->cullMode = QQuickShaderEffectMaterial::CullMode(m_cullMode); node->markDirty(QSGNode::DirtyMaterial); } - if (m_dirtyData) { - QVector<QPair<QByteArray, QVariant> > values; - QVector<QPair<QByteArray, QSGTextureProvider *> > textures; - const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = material->textureProviders(); + if (m_dirtyProgram) { + Key s = m_common.source; + if (s.sourceCode[Key::FragmentShader].isEmpty()) + s.sourceCode[Key::FragmentShader] = qt_default_fragment_code; + if (s.sourceCode[Key::VertexShader].isEmpty()) + s.sourceCode[Key::VertexShader] = qt_default_vertex_code; + s.className = metaObject()->className(); - for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin(); - it != m_source.uniformNames.end(); ++it) { - values.append(qMakePair(*it, property(*it))); - } - for (int i = 0; i < oldTextures.size(); ++i) { - QSGTextureProvider *t = oldTextures.at(i).second; - if (t) { - disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); - } - } - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0; - textures.append(qMakePair(source.name, t)); - if (t) { - Q_ASSERT_X(t->thread() == QThread::currentThread(), - "QQuickShaderEffect::updatePaintNode", - "Texture provider must belong to the rendering thread"); - connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - connect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*))); - } - } - material->setUniforms(values); - material->setTextureProviders(textures); + material->setProgramSource(s); + material->attributes = m_common.attributes; node->markDirty(QSGNode::DirtyMaterial); - m_dirtyData = false; + m_dirtyProgram = false; + m_dirtyUniforms = true; + } + + if (m_dirtyUniforms || m_dirtyUniformValues || m_dirtyTextureProviders) { + m_common.updateMaterial(node, material, m_dirtyUniforms, m_dirtyUniformValues, + m_dirtyTextureProviders); + node->markDirty(QSGNode::DirtyMaterial); + m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; } return node; } +void QQuickShaderEffect::componentComplete() +{ + m_common.updateShader(this, Key::VertexShader); + m_common.updateShader(this, Key::FragmentShader); + QQuickItem::componentComplete(); +} + void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &value) { - if (change == QQuickItem::ItemSceneChange) { - // See comment in QQuickShaderEffect::setSource(). - if (value.canvas) { - for (int i = 0; i < m_sources.size(); ++i) { - if (m_sources.at(i).sourceObject) - QQuickItemPrivate::get(m_sources.at(i).sourceObject)->refCanvas(value.canvas); - } - } else { - for (int i = 0; i < m_sources.size(); ++i) { - if (m_sources.at(i).sourceObject) - QQuickItemPrivate::get(m_sources.at(i).sourceObject)->derefCanvas(); - } - } - } + if (change == QQuickItem::ItemSceneChange) + m_common.updateCanvas(value.canvas); QQuickItem::itemChange(change, value); } - QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index db1e4e78c1..32f12fad30 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -62,6 +62,34 @@ class QSGContext; class QSignalMapper; class QQuickCustomMaterialShader; +// Common class for QQuickShaderEffect and QQuickCustomParticle. +struct QQuickShaderEffectCommon +{ + typedef QQuickShaderEffectMaterialKey Key; + typedef QQuickShaderEffectMaterial::UniformData UniformData; + + ~QQuickShaderEffectCommon(); + void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); + void connectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); + void updateParseLog(bool ignoreAttributes); + void lookThroughShaderCode(QQuickItem *item, Key::ShaderType shaderType, const QByteArray &code); + void updateShader(QQuickItem *item, Key::ShaderType shaderType); + void updateMaterial(QQuickShaderEffectNode *node, QQuickShaderEffectMaterial *material, + bool updateUniforms, bool updateUniformValues, bool updateTextureProviders); + void updateCanvas(QQuickCanvas *canvas); + + // Called by slots in QQuickShaderEffect: + void sourceDestroyed(QObject *object); + void propertyChanged(QQuickItem *item, int mappedId, bool *textureProviderChanged); + + Key source; + QVector<QByteArray> attributes; + QVector<UniformData> uniformData[Key::ShaderTypeCount]; + QVector<QSignalMapper *> signalMappers[Key::ShaderTypeCount]; + QString parseLog; +}; + + class Q_AUTOTEST_EXPORT QQuickShaderEffect : public QQuickItem { Q_OBJECT @@ -93,10 +121,10 @@ public: QQuickShaderEffect(QQuickItem *parent = 0); ~QQuickShaderEffect(); - QByteArray fragmentShader() const { return m_source.fragmentCode; } + QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; } void setFragmentShader(const QByteArray &code); - QByteArray vertexShader() const { return m_source.vertexCode; } + QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; } void setVertexShader(const QByteArray &code); bool blending() const { return m_blending; } @@ -111,8 +139,9 @@ public: QString log() const { return m_log; } Status status() const { return m_status; } - void ensureCompleted(); - QString parseLog() { return m_parseLog; } + QString parseLog(); + + virtual bool event(QEvent *); Q_SIGNALS: void fragmentShaderChanged(); @@ -126,27 +155,22 @@ Q_SIGNALS: protected: virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual void componentComplete(); virtual void itemChange(ItemChange change, const ItemChangeData &value); private Q_SLOTS: - void changeSource(int index); - void updateData(); void updateGeometry(); void updateLogAndStatus(const QString &log, int status); void sourceDestroyed(QObject *object); + void propertyChanged(int mappedId); private: friend class QQuickCustomMaterialShader; friend class QQuickShaderEffectNode; - void setSource(const QVariant &var, int index); - void disconnectPropertySignals(); - void connectPropertySignals(); - void reset(); - void updateProperties(); - void lookThroughShaderCode(const QByteArray &code); + typedef QQuickShaderEffectMaterialKey Key; + typedef QQuickShaderEffectMaterial::UniformData UniformData; - QQuickShaderEffectProgram m_source; QSize m_meshResolution; QQuickShaderEffectMesh *m_mesh; QQuickGridMesh m_defaultMesh; @@ -154,23 +178,16 @@ private: QString m_log; Status m_status; - struct SourceData - { - QSignalMapper *mapper; - QQuickItem *sourceObject; - QByteArray name; - }; - QVector<SourceData> m_sources; - QString m_parseLog; + QQuickShaderEffectCommon m_common; uint m_blending : 1; - uint m_dirtyData : 1; - - uint m_programDirty : 1; + uint m_dirtyUniforms : 1; + uint m_dirtyUniformValues : 1; + uint m_dirtyTextureProviders : 1; + uint m_dirtyProgram : 1; + uint m_dirtyParseLog : 1; uint m_dirtyMesh : 1; uint m_dirtyGeometry : 1; - - uint m_complete : 1; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index c4b91844e0..a3cadb97a2 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -60,7 +60,6 @@ protected: friend class QQuickShaderEffectNode; virtual void compile(); - virtual void initialize(); virtual const char *vertexShader() const; virtual const char *fragmentShader() const; @@ -70,17 +69,15 @@ protected: QString m_log; bool m_compiled; - QVector<int> m_uniformLocs; - int m_opacityLoc; - int m_matrixLoc; - uint m_textureIndicesSet; + QVector<int> m_uniformLocs[QQuickShaderEffectMaterialKey::ShaderTypeCount]; + uint m_initialized : 1; }; QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes) : m_key(key) , m_attributes(attributes) , m_compiled(false) - , m_textureIndicesSet(false) + , m_initialized(false) { for (int i = 0; i < attributes.count(); ++i) m_attributeNames.append(attributes.at(i).constData()); @@ -104,24 +101,25 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri : QQuickShaderEffect::Error); } - if (!m_textureIndicesSet) { - for (int i = 0; i < material->m_textures.size(); ++i) - program()->setUniformValue(material->m_textures.at(i).first.constData(), i); - m_textureIndicesSet = true; - } + if (!m_initialized) { + for (int i = 0; i < material->textureProviders.size(); ++i) + program()->setUniformValue(material->textureProviders.at(i).first.constData(), i); - if (m_uniformLocs.size() != material->m_uniformValues.size()) { - m_uniformLocs.reserve(material->m_uniformValues.size()); - for (int i = 0; i < material->m_uniformValues.size(); ++i) { - const QByteArray &name = material->m_uniformValues.at(i).first; - m_uniformLocs.append(program()->uniformLocation(name.constData())); + for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { + Q_ASSERT(m_uniformLocs[shaderType].isEmpty()); + m_uniformLocs[shaderType].reserve(material->uniforms[shaderType].size()); + for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { + const QByteArray &name = material->uniforms[shaderType].at(i).name; + m_uniformLocs[shaderType].append(program()->uniformLocation(name.constData())); + } } + m_initialized = true; } QOpenGLFunctions *functions = state.context()->functions(); - for (int i = material->m_textures.size() - 1; i >= 0; --i) { + for (int i = material->textureProviders.size() - 1; i >= 0; --i) { functions->glActiveTexture(GL_TEXTURE0 + i); - if (QSGTextureProvider *provider = material->m_textures.at(i).second) { + if (QSGTextureProvider *provider = material->textureProviders.at(i).second) { if (QSGTexture *texture = provider->texture()) { texture->bind(); continue; @@ -131,57 +129,67 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri glBindTexture(GL_TEXTURE_2D, 0); } - if (material->m_source.respectsOpacity) - program()->setUniformValue(m_opacityLoc, state.opacity()); - - for (int i = 0; i < material->m_uniformValues.count(); ++i) { - const QVariant &v = material->m_uniformValues.at(i).second; - - switch (v.type()) { - case QMetaType::QColor: - program()->setUniformValue(m_uniformLocs.at(i), qt_premultiply_color(qvariant_cast<QColor>(v))); - break; - case QMetaType::Float: - program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<float>(v)); - break; - case QMetaType::Double: - program()->setUniformValue(m_uniformLocs.at(i), (float) qvariant_cast<double>(v)); - break; - case QMetaType::QTransform: - program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QTransform>(v)); - break; - case QMetaType::Int: - program()->setUniformValue(m_uniformLocs.at(i), v.toInt()); - break; - case QMetaType::Bool: - program()->setUniformValue(m_uniformLocs.at(i), GLint(v.toBool())); - break; - case QMetaType::QSize: - case QMetaType::QSizeF: - program()->setUniformValue(m_uniformLocs.at(i), v.toSizeF()); - break; - case QMetaType::QPoint: - case QMetaType::QPointF: - program()->setUniformValue(m_uniformLocs.at(i), v.toPointF()); - break; - case QMetaType::QRect: - case QMetaType::QRectF: - { - QRectF r = v.toRectF(); - program()->setUniformValue(m_uniformLocs.at(i), r.x(), r.y(), r.width(), r.height()); + for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < material->uniforms[shaderType].size(); ++i) { + const QQuickShaderEffectMaterial::UniformData &d = material->uniforms[shaderType].at(i); + int loc = m_uniformLocs[shaderType].at(i); + + if (d.specialType == QQuickShaderEffectMaterial::UniformData::Opacity) { + program()->setUniformValue(loc, state.opacity()); + } if (d.specialType == QQuickShaderEffectMaterial::UniformData::Matrix) { + if (state.isMatrixDirty()) + program()->setUniformValue(loc, state.combinedMatrix()); + } else { + switch (d.value.type()) { + case QMetaType::QColor: + program()->setUniformValue(loc, qt_premultiply_color(qvariant_cast<QColor>(d.value))); + break; + case QMetaType::Float: + program()->setUniformValue(loc, qvariant_cast<float>(d.value)); + break; + case QMetaType::Double: + program()->setUniformValue(loc, (float) qvariant_cast<double>(d.value)); + break; + case QMetaType::QTransform: + program()->setUniformValue(loc, qvariant_cast<QTransform>(d.value)); + break; + case QMetaType::Int: + program()->setUniformValue(loc, d.value.toInt()); + break; + case QMetaType::Bool: + program()->setUniformValue(loc, GLint(d.value.toBool())); + break; + case QMetaType::QSize: + case QMetaType::QSizeF: + program()->setUniformValue(loc, d.value.toSizeF()); + break; + case QMetaType::QPoint: + case QMetaType::QPointF: + program()->setUniformValue(loc, d.value.toPointF()); + break; + case QMetaType::QRect: + case QMetaType::QRectF: + { + QRectF r = d.value.toRectF(); + program()->setUniformValue(loc, r.x(), r.y(), r.width(), r.height()); + } + break; + case QMetaType::QVector3D: + program()->setUniformValue(loc, qvariant_cast<QVector3D>(d.value)); + break; + case QMetaType::QVector4D: + program()->setUniformValue(loc, qvariant_cast<QVector4D>(d.value)); + break; + default: + break; + } } - break; - case QMetaType::QVector3D: - program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QVector3D>(v)); - break; - default: - break; } } const QQuickShaderEffectMaterial *oldMaterial = static_cast<const QQuickShaderEffectMaterial *>(oldEffect); - if (oldEffect == 0 || material->cullMode() != oldMaterial->cullMode()) { - switch (material->cullMode()) { + if (oldEffect == 0 || material->cullMode != oldMaterial->cullMode) { + switch (material->cullMode) { case QQuickShaderEffectMaterial::FrontFaceCulling: glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); @@ -195,9 +203,6 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri break; } } - - if ((state.isMatrixDirty()) && material->m_source.respectsMatrix) - program()->setUniformValue(m_matrixLoc, state.combinedMatrix()); } char const *const *QQuickCustomMaterialShader::attributeNames() const @@ -277,38 +282,42 @@ void QQuickCustomMaterialShader::compile() } } -void QQuickCustomMaterialShader::initialize() -{ - m_opacityLoc = program()->uniformLocation("qt_Opacity"); - m_matrixLoc = program()->uniformLocation("qt_Matrix"); -} - const char *QQuickCustomMaterialShader::vertexShader() const { - return m_key.vertexCode.constData(); + return m_key.sourceCode[QQuickShaderEffectMaterialKey::VertexShader].constData(); } const char *QQuickCustomMaterialShader::fragmentShader() const { - return m_key.fragmentCode.constData(); + return m_key.sourceCode[QQuickShaderEffectMaterialKey::FragmentShader].constData(); } bool QQuickShaderEffectMaterialKey::operator == (const QQuickShaderEffectMaterialKey &other) const { - return vertexCode == other.vertexCode && fragmentCode == other.fragmentCode && className == other.className; + if (className != other.className) + return false; + for (int shaderType = 0; shaderType < ShaderTypeCount; ++shaderType) { + if (sourceCode[shaderType] != other.sourceCode[shaderType]) + return false; + } + return true; } uint qHash(const QQuickShaderEffectMaterialKey &key) { - return qHash(qMakePair(qMakePair(key.vertexCode, key.fragmentCode), key.className)); + uint hash = qHash((void *)key.className); + typedef QQuickShaderEffectMaterialKey Key; + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) + hash = hash * 31337 + qHash(key.sourceCode[shaderType]); + return hash; } QHash<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QQuickShaderEffectMaterial::materialMap; QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node) - : m_cullMode(NoCulling) + : cullMode(NoCulling) , m_node(node) , m_emittedLogChanged(false) { @@ -322,7 +331,7 @@ QSGMaterialType *QQuickShaderEffectMaterial::type() const QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const { - return new QQuickCustomMaterialShader(m_source, m_source.attributeNames); + return new QQuickCustomMaterialShader(m_source, attributes); } int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const @@ -330,17 +339,7 @@ int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const return this - static_cast<const QQuickShaderEffectMaterial *>(other); } -void QQuickShaderEffectMaterial::setCullMode(QQuickShaderEffectMaterial::CullMode face) -{ - m_cullMode = face; -} - -QQuickShaderEffectMaterial::CullMode QQuickShaderEffectMaterial::cullMode() const -{ - return m_cullMode; -} - -void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgram &source) +void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source) { m_source = source; m_emittedLogChanged = false; @@ -351,25 +350,10 @@ void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectProgra } } -void QQuickShaderEffectMaterial::setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues) -{ - m_uniformValues = uniformValues; -} - -void QQuickShaderEffectMaterial::setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures) -{ - m_textures = textures; -} - -const QVector<QPair<QByteArray, QSGTextureProvider *> > &QQuickShaderEffectMaterial::textureProviders() const -{ - return m_textures; -} - void QQuickShaderEffectMaterial::updateTextures() const { - for (int i = 0; i < m_textures.size(); ++i) { - if (QSGTextureProvider *provider = m_textures.at(i).second) { + for (int i = 0; i < textureProviders.size(); ++i) { + if (QSGTextureProvider *provider = textureProviders.at(i).second) { if (QSGDynamicTexture *texture = qobject_cast<QSGDynamicTexture *>(provider->texture())) texture->updateTexture(); } @@ -378,18 +362,16 @@ void QQuickShaderEffectMaterial::updateTextures() const void QQuickShaderEffectMaterial::invalidateTextureProvider(QSGTextureProvider *provider) { - for (int i = 0; i < m_textures.size(); ++i) { - if (provider == m_textures.at(i).second) - m_textures[i].second = 0; + for (int i = 0; i < textureProviders.size(); ++i) { + if (provider == textureProviders.at(i).second) + textureProviders[i].second = 0; } } QQuickShaderEffectNode::QQuickShaderEffectNode() - : m_material(this) { QSGNode::setFlag(UsePreprocess, true); - setMaterial(&m_material); #ifdef QML_RUNTIME_TESTING description = QLatin1String("shadereffect"); @@ -407,8 +389,8 @@ void QQuickShaderEffectNode::markDirtyTexture() void QQuickShaderEffectNode::textureProviderDestroyed(QObject *object) { - Q_ASSERT(qobject_cast<QSGTextureProvider *>(object)); - m_material.invalidateTextureProvider(static_cast<QSGTextureProvider *>(object)); + Q_ASSERT(material()); + static_cast<QQuickShaderEffectMaterial *>(material())->invalidateTextureProvider(static_cast<QSGTextureProvider *>(object)); } void QQuickShaderEffectNode::preprocess() diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index e22d2de9e2..1bbce86426 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -55,8 +55,14 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE struct QQuickShaderEffectMaterialKey { - QByteArray vertexCode; - QByteArray fragmentCode; + enum ShaderType + { + VertexShader, + FragmentShader, + ShaderTypeCount + }; + + QByteArray sourceCode[ShaderTypeCount]; const char *className; bool operator == (const QQuickShaderEffectMaterialKey &other) const; @@ -64,24 +70,21 @@ struct QQuickShaderEffectMaterialKey { uint qHash(const QQuickShaderEffectMaterialKey &key); -// TODO: Implement support for multisampling. -struct QQuickShaderEffectProgram : public QQuickShaderEffectMaterialKey -{ - QQuickShaderEffectProgram() : respectsOpacity(false), respectsMatrix(false) {} - - QVector<QByteArray> attributeNames; - QSet<QByteArray> uniformNames; - - uint respectsOpacity : 1; - uint respectsMatrix : 1; -}; - class QQuickCustomMaterialShader; class QQuickShaderEffectNode; class QQuickShaderEffectMaterial : public QSGMaterial { public: + struct UniformData + { + enum SpecialType { None, Sampler, Opacity, Matrix }; + + QByteArray name; + QVariant value; + SpecialType specialType; + }; + enum CullMode { NoCulling, @@ -94,13 +97,12 @@ public: virtual QSGMaterialShader *createShader() const; virtual int compare(const QSGMaterial *other) const; - void setCullMode(CullMode face); - CullMode cullMode() const; + QVector<QByteArray> attributes; + QVector<UniformData> uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount]; + QVector<QPair<QByteArray, QSGTextureProvider *> > textureProviders; + CullMode cullMode; - void setProgramSource(const QQuickShaderEffectProgram &); - void setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues); - void setTextureProviders(const QVector<QPair<QByteArray, QSGTextureProvider *> > &textures); - const QVector<QPair<QByteArray, QSGTextureProvider *> > &textureProviders() const; + void setProgramSource(const QQuickShaderEffectMaterialKey &source); void updateTextures() const; void invalidateTextureProvider(QSGTextureProvider *provider); @@ -114,11 +116,8 @@ protected: // one. To guarantee that the type pointer is unique, the type object must live as long as // there are any CustomMaterialShaders of that type. QSharedPointer<QSGMaterialType> m_type; + QQuickShaderEffectMaterialKey m_source; - QQuickShaderEffectProgram m_source; - QVector<QPair<QByteArray, QVariant> > m_uniformValues; - QVector<QPair<QByteArray, QSGTextureProvider *> > m_textures; - CullMode m_cullMode; QQuickShaderEffectNode *m_node; bool m_emittedLogChanged; @@ -137,17 +136,12 @@ public: virtual void preprocess(); - QQuickShaderEffectMaterial *shaderMaterial() { return &m_material; } - Q_SIGNALS: void logAndStatusChanged(const QString &, int status); private Q_SLOTS: void markDirtyTexture(); void textureProviderDestroyed(QObject *object); - -private: - QQuickShaderEffectMaterial m_material; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index d4ddbc400d..900cb84de8 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -389,6 +389,7 @@ QImage QQuickSpriteEngine::assembledImage() else qmlInfo(state) << "SpriteEngine: Animations too large to fit in one texture, pushed over the edge by:" << state->source().toLocalFile(); qmlInfo(state) << "SpriteEngine: Your texture max size today is " << maxSize; + return QImage(); } state->m_generatedCount = rowsNeeded; h += state->frameHeight() * rowsNeeded; @@ -407,7 +408,7 @@ QImage QQuickSpriteEngine::assembledImage() QPainter p(&image); int y = 0; foreach (QQuickSprite* state, m_sprites){ - QImage img(state->source().toLocalFile()); + QImage img(state->m_pix.image()); int frameWidth = state->m_frameWidth; int frameHeight = state->m_frameHeight; if (img.height() == frameHeight && img.width() < maxSize){//Simple case diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index e1a28a466a..24dd10ac9f 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -748,7 +748,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) QTextLine line; int visibleCount = 0; bool elide; - bool widthChanged; qreal height = 0; QString elideText; bool once = true; @@ -776,7 +775,6 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) bool truncateHeight = false; truncated = false; elide = false; - widthChanged = false; int characterCount = 0; int unwrappedLineCount = 1; int maxLineCount = maximumLineCount(); @@ -911,10 +909,8 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const naturalWidth) const qreal oldWidth = lineWidth; lineWidth = q->widthValid() && q->width() > 0 ? q->width() : FLT_MAX; - if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit)) { - widthChanged = true; + if (lineWidth != oldWidth && (singlelineElide || multilineElide || canWrap || horizontalFit)) continue; - } } // If the next needs to be elided and there's an abbreviated string available @@ -2363,9 +2359,6 @@ void QQuickText::componentComplete() QQuickItem::componentComplete(); if (d->updateOnComponentComplete) d->updateLayout(); - - // Enable accessibility for text items. - d->setAccessibleFlagAndListener(); } diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index eefe938467..739b5f859b 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -44,6 +44,7 @@ #ifndef QT_NO_TEXTCONTROL +#include <qcoreapplication.h> #include <qfont.h> #include <qpainter.h> #include <qevent.h> @@ -107,11 +108,12 @@ QQuickTextControlPrivate::QQuickTextControlPrivate() ignoreAutomaticScrollbarAdjustement(false), overwriteMode(false), acceptRichText(true), - hideCursor(false), + cursorVisible(false), hasFocus(false), isEnabled(true), hadSelectionOnMousePress(false), - wordSelectionEnabled(false) + wordSelectionEnabled(false), + hasImState(false) {} bool QQuickTextControlPrivate::cursorMoveKeyEvent(QKeyEvent *e) @@ -292,6 +294,8 @@ void QQuickTextControlPrivate::setContent(Qt::TextFormat format, const QString & { Q_Q(QQuickTextControl); + cancelPreedit(); + // for use when called from setPlainText. we may want to re-use the currently // set char format then. const QTextCharFormat charFormatForInsertion = cursor.charFormat(); @@ -1441,13 +1445,16 @@ void QQuickTextControlPrivate::inputMethodEvent(QInputMethodEvent *e) QList<QTextLayout::FormatRange> overrides; const int oldPreeditCursor = preeditCursor; preeditCursor = e->preeditString().length(); - hideCursor = false; + hasImState = !e->preeditString().isEmpty(); + cursorVisible = true; for (int i = 0; i < e->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = e->attributes().at(i); if (a.type == QInputMethodEvent::Cursor) { + hasImState = true; preeditCursor = a.start; - hideCursor = !a.length; + cursorVisible = a.length != 0; } else if (a.type == QInputMethodEvent::TextFormat) { + hasImState = true; QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); if (f.isValid()) { QTextLayout::FormatRange o; @@ -1514,6 +1521,37 @@ void QQuickTextControlPrivate::focusEvent(QFocusEvent *e) } } +bool QQuickTextControl::hasImState() const +{ + Q_D(const QQuickTextControl); + return d->hasImState; +} + +bool QQuickTextControl::cursorVisible() const +{ + Q_D(const QQuickTextControl); + return d->cursorVisible; +} + +void QQuickTextControl::setCursorVisible(bool visible) +{ + Q_D(QQuickTextControl); + d->cursorVisible = visible; + d->setBlinkingCursorEnabled(d->cursorVisible + && (d->interactionFlags & (Qt::TextEditable | Qt::TextSelectableByKeyboard))); +} + +QTextCursor QQuickTextControl::cursorForPosition(const QPointF &pos) const +{ + Q_D(const QQuickTextControl); + int cursorPos = hitTest(pos, Qt::FuzzyHit); + if (cursorPos == -1) + cursorPos = 0; + QTextCursor c(d->doc); + c.setPosition(cursorPos); + return c; +} + QRectF QQuickTextControl::cursorRect(const QTextCursor &cursor) const { Q_D(const QQuickTextControl); @@ -1727,21 +1765,31 @@ bool QQuickTextControlPrivate::isPreediting() const void QQuickTextControlPrivate::commitPreedit() { - if (!isPreediting()) + Q_Q(QQuickTextControl); + + if (!hasImState) return; qApp->inputMethod()->commit(); - if (!isPreediting()) + if (!hasImState) return; - cursor.beginEditBlock(); - preeditCursor = 0; - QTextBlock block = cursor.block(); - QTextLayout *layout = block.layout(); - layout->setPreeditArea(-1, QString()); - layout->clearAdditionalFormats(); - cursor.endEditBlock(); + QInputMethodEvent event; + QCoreApplication::sendEvent(q->parent(), &event); +} + +void QQuickTextControlPrivate::cancelPreedit() +{ + Q_Q(QQuickTextControl); + + if (!hasImState) + return; + + qApp->inputMethod()->reset(); + + QInputMethodEvent event; + QCoreApplication::sendEvent(q->parent(), &event); } void QQuickTextControl::setTextInteractionFlags(Qt::TextInteractionFlags flags) diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 9e3fc90eb1..be3f7f7ccf 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -98,6 +98,10 @@ public: QString toHtml() const; #endif + bool hasImState() const; + bool cursorVisible() const; + void setCursorVisible(bool visible); + QTextCursor cursorForPosition(const QPointF &pos) const; QRectF cursorRect(const QTextCursor &cursor) const; QRectF cursorRect() const; QRectF selectionRect(const QTextCursor &cursor) const; diff --git a/src/quick/items/qquicktextcontrol_p_p.h b/src/quick/items/qquicktextcontrol_p_p.h index c5a39cc759..3a10f007be 100644 --- a/src/quick/items/qquicktextcontrol_p_p.h +++ b/src/quick/items/qquicktextcontrol_p_p.h @@ -127,6 +127,7 @@ public: bool isPreediting() const; void commitPreedit(); + void cancelPreedit(); QPointF trippleClickPoint; QPointF mousePressPos; @@ -155,11 +156,12 @@ public: bool ignoreAutomaticScrollbarAdjustement : 1; bool overwriteMode : 1; bool acceptRichText : 1; - bool hideCursor : 1; // used to hide the cursor in the preedit area + bool cursorVisible : 1; // used to hide the cursor in the preedit area bool hasFocus : 1; bool isEnabled : 1; bool hadSelectionOnMousePress : 1; bool wordSelectionEnabled : 1; + bool hasImState : 1; void _q_copyLink(); void _q_updateBlock(const QTextBlock &); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 4fa5233b9a..f727c54322 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -871,10 +871,9 @@ void QQuickTextEdit::setCursorVisible(bool on) if (d->cursorVisible == on) return; d->cursorVisible = on; - QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut); if (!on && !d->persistentSelection) d->control->setCursorIsFocusIndicator(true); - d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); + d->control->setCursorVisible(on); emit cursorVisibleChanged(d->cursorVisible); } @@ -1536,15 +1535,18 @@ void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event) Q_D(QQuickTextEdit); const bool wasComposing = isInputMethodComposing(); d->control->processEvent(event, QPointF(0, -d->yoff)); + setCursorVisible(d->control->cursorVisible()); if (wasComposing != isInputMethodComposing()) emit inputMethodComposingChanged(); } void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value) { + Q_D(QQuickTextEdit); if (change == ItemActiveFocusHasChanged) { setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus()); - + QFocusEvent focusEvent(value.boolValue ? QEvent::FocusIn : QEvent::FocusOut); + d->control->processEvent(&focusEvent, QPointF(0, -d->yoff)); if (value.boolValue) { q_updateAlignment(); connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), @@ -1729,9 +1731,7 @@ bool QQuickTextEdit::canRedo() const bool QQuickTextEdit::isInputMethodComposing() const { Q_D(const QQuickTextEdit); - if (QTextLayout *layout = d->control->textCursor().block().layout()) - return layout->preeditAreaText().length() > 0; - return false; + return d->control->hasImState(); } void QQuickTextEditPrivate::init() diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index f2da67bae7..5aa01d27f3 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -45,6 +45,7 @@ #include <private/qqmlglobal_p.h> +#include <QtCore/qcoreapplication.h> #include <QtQml/qqmlinfo.h> #include <QtGui/qevent.h> #include <QTextBoundaryFinder> @@ -62,10 +63,6 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) -#ifdef QT_GUI_PASSWORD_ECHO_DELAY -static const int qt_passwordEchoDelay = QT_GUI_PASSWORD_ECHO_DELAY; -#endif - /*! \qmlclass TextInput QQuickTextInput \inqmlmodule QtQuick 2 @@ -128,8 +125,8 @@ void QQuickTextInput::setText(const QString &s) Q_D(QQuickTextInput); if (s == text()) return; - if (d->composeMode()) - qApp->inputMethod()->reset(); + + d->cancelPreedit(); d->internalSetText(s, -1, false); } @@ -678,9 +675,7 @@ QRectF QQuickTextInput::cursorRectangle() const { Q_D(const QQuickTextInput); - int c = d->m_cursor; - if (d->m_preeditCursor != -1) - c += d->m_preeditCursor; + int c = d->m_cursor + d->m_preeditCursor; if (d->m_echoMode == NoEcho) c = 0; QTextLine l = d->m_textLayout.lineForTextPosition(c); @@ -1402,7 +1397,7 @@ void QQuickTextInput::keyPressEvent(QKeyEvent* ev) void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev) { Q_D(QQuickTextInput); - const bool wasComposing = d->preeditAreaText().length() > 0; + const bool wasComposing = d->hasImState; if (d->m_readOnly) { ev->ignore(); } else { @@ -1411,7 +1406,7 @@ void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev) if (!ev->isAccepted()) QQuickImplicitSizeItem::inputMethodEvent(ev); - if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0)) + if (wasComposing != d->hasImState) emit inputMethodComposingChanged(); } @@ -1929,11 +1924,11 @@ void QQuickTextInput::redo() void QQuickTextInput::insert(int position, const QString &text) { Q_D(QQuickTextInput); -#ifdef QT_GUI_PASSWORD_ECHO_DELAY - if (d->m_echoMode == QQuickTextInput::Password) - d->m_passwordEchoTimer.start(qt_passwordEchoDelay, this); -#endif - + if (d->m_echoMode == QQuickTextInput::Password) { + int delay = qGuiApp->styleHints()->passwordMaskDelay(); + if (delay > 0) + d->m_passwordEchoTimer.start(delay, this); + } if (position < 0 || position > d->m_text.length()) return; @@ -2484,11 +2479,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) if (change == ItemActiveFocusHasChanged) { bool hasFocus = value.boolValue; setCursorVisible(hasFocus); // ### refactor: && d->canvas && d->canvas->hasFocus() -#ifdef QT_GUI_PASSWORD_ECHO_DELAY if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) { -#else - if (!hasFocus && d->m_passwordEchoEditing) { -#endif d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events } @@ -2521,7 +2512,7 @@ void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) bool QQuickTextInput::isInputMethodComposing() const { Q_D(const QQuickTextInput); - return d->preeditAreaText().length() > 0; + return d->hasImState; } void QQuickTextInputPrivate::init() @@ -2660,7 +2651,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate) if (m_echoMode == QQuickTextInput::Password) { str.fill(m_passwordCharacter); -#ifdef QT_GUI_PASSWORD_ECHO_DELAY if (m_passwordEchoTimer.isActive() && m_cursor > 0 && m_cursor <= m_text.length()) { int cursor = m_cursor - 1; QChar uc = m_text.at(cursor); @@ -2673,7 +2663,6 @@ void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate) str[cursor - 1] = uc; } } -#endif } else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) { str.fill(m_passwordCharacter); } @@ -2830,18 +2819,31 @@ void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode) */ void QQuickTextInputPrivate::commitPreedit() { - if (!composeMode()) + Q_Q(QQuickTextInput); + + if (!hasImState) return; qApp->inputMethod()->commit(); - if (!composeMode()) + if (!hasImState) + return; + + QInputMethodEvent ev; + QCoreApplication::sendEvent(q, &ev); +} + +void QQuickTextInputPrivate::cancelPreedit() +{ + Q_Q(QQuickTextInput); + + if (!hasImState) return; - m_preeditCursor = 0; - m_textLayout.setPreeditArea(-1, QString()); - m_textLayout.clearAdditionalFormats(); - updateLayout(); + qApp->inputMethod()->reset(); + + QInputMethodEvent ev; + QCoreApplication::sendEvent(q, &ev); } /*! @@ -3123,15 +3125,19 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) m_textLayout.setPreeditArea(m_cursor, event->preeditString()); #endif //QT_NO_IM const int oldPreeditCursor = m_preeditCursor; + const bool oldCursorVisible = cursorVisible; m_preeditCursor = event->preeditString().length(); - m_hideCursor = false; + hasImState = !event->preeditString().isEmpty(); + cursorVisible = true; QList<QTextLayout::FormatRange> formats; for (int i = 0; i < event->attributes().size(); ++i) { const QInputMethodEvent::Attribute &a = event->attributes().at(i); if (a.type == QInputMethodEvent::Cursor) { + hasImState = true; m_preeditCursor = a.start; - m_hideCursor = !a.length; + cursorVisible = a.length != 0; } else if (a.type == QInputMethodEvent::TextFormat) { + hasImState = true; QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat(); if (f.isValid()) { QTextLayout::FormatRange o; @@ -3154,6 +3160,9 @@ void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event) if (isGettingInput) finishChange(priorState); + if (cursorVisible != oldCursorVisible) + emit q->cursorVisibleChanged(cursorVisible); + if (selectionChange) { emit q->selectionChanged(); q->updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorPosition @@ -3333,11 +3342,12 @@ void QQuickTextInputPrivate::addCommand(const Command &cmd) */ void QQuickTextInputPrivate::internalInsert(const QString &s) { -#ifdef QT_GUI_PASSWORD_ECHO_DELAY Q_Q(QQuickTextInput); - if (m_echoMode == QQuickTextInput::Password) - m_passwordEchoTimer.start(qt_passwordEchoDelay, q); -#endif + if (m_echoMode == QQuickTextInput::Password) { + int delay = qGuiApp->styleHints()->passwordMaskDelay(); + if (delay > 0) + m_passwordEchoTimer.start(delay, q); + } if (hasSelectedText()) addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend)); if (m_maskData) { @@ -3962,11 +3972,9 @@ void QQuickTextInput::timerEvent(QTimerEvent *event) d->m_blinkStatus = !d->m_blinkStatus; d->updateType = QQuickTextInputPrivate::UpdatePaintNode; update(); -#ifdef QT_GUI_PASSWORD_ECHO_DELAY } else if (event->timerId() == d->m_passwordEchoTimer.timerId()) { d->m_passwordEchoTimer.stop(); d->updateDisplayText(); -#endif } } diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 3bd34b2661..1bc2cf548b 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -115,7 +115,7 @@ public: , selectPressed(false) , textLayoutDirty(true) , persistentSelection(false) - , m_hideCursor(false) + , hasImState(false) , m_separator(0) , m_readOnly(0) , m_textDirty(0) @@ -202,9 +202,7 @@ public: QColor selectionColor; QColor selectedTextColor; -#ifdef QT_GUI_PASSWORD_ECHO_DELAY QBasicTimer m_passwordEchoTimer; -#endif int lastSelectionStart; int lastSelectionEnd; int m_cursor; @@ -247,7 +245,7 @@ public: bool selectPressed:1; bool textLayoutDirty:1; bool persistentSelection:1; - bool m_hideCursor : 1; // used to hide the m_cursor inside preedit areas + bool hasImState : 1; bool m_separator : 1; bool m_readOnly : 1; bool m_textDirty : 1; @@ -321,6 +319,7 @@ public: #endif void commitPreedit(); + void cancelPreedit(); Qt::CursorMoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); } void setCursorMoveStyle(Qt::CursorMoveStyle style) { m_textLayout.setCursorMoveStyle(style); } @@ -378,9 +377,7 @@ public: void updatePasswordEchoEditing(bool editing); void cancelPasswordEchoTimer() { -#ifdef QT_GUI_PASSWORD_ECHO_DELAY m_passwordEchoTimer.stop(); -#endif } Qt::LayoutDirection layoutDirection() const { diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 5be4963809..81340d6e66 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -254,9 +254,6 @@ namespace { decorations |= (backgroundColor.isValid() ? QQuickTextNode::Background : QQuickTextNode::NoDecoration); qreal ascent = glyphRun.rawFont().ascent(); - // ### QTBUG-22919 The bounding rect returned by QGlyphRun appears to start on the - // baseline, move it by the ascent so all bounding rects are at baseline - ascent. - searchRect.translate(0, -ascent); insert(binaryTree, BinaryTreeNode(glyphRun, selectionState, searchRect, decorations, textColor, backgroundColor, position, ascent)); } @@ -1219,12 +1216,6 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex int textPos = block.position(); QTextBlock::iterator blockIterator = block.begin(); - if (blockIterator.atEnd() && preeditLength) { - engine.setPosition(blockPosition); - textPos = engine.addText(block, block.charFormat(), textColor, colorChanges, - textPos, textPos + preeditLength, - selectionStart, selectionEnd); - } while (!blockIterator.atEnd()) { QTextFragment fragment = blockIterator.fragment(); @@ -1275,6 +1266,19 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex ++blockIterator; } + if (preeditLength >= 0 && textPos <= block.position() + preeditPosition) { + engine.setPosition(blockPosition); + textPos = block.position() + preeditPosition; + QTextLine line = block.layout()->lineForTextPosition(preeditPosition); + if (!engine.currentLine().isValid() + || line.lineNumber() != engine.currentLine().lineNumber()) { + engine.setCurrentLine(line); + } + textPos = engine.addText(block, block.charFormat(), textColor, colorChanges, + textPos, textPos + preeditLength, + selectionStart, selectionEnd); + } + engine.setCurrentLine(QTextLine()); // Reset current line because the text layout changed ++it; } diff --git a/src/quick/items/qquickvisualdatamodel.cpp b/src/quick/items/qquickvisualdatamodel.cpp index 88d46c60ee..cea83969f3 100644 --- a/src/quick/items/qquickvisualdatamodel.cpp +++ b/src/quick/items/qquickvisualdatamodel.cpp @@ -803,7 +803,7 @@ QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index } if (cacheItem->incubationTask) { - if (!asynchronous) { + if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { // previously requested async - now needed immediately cacheItem->incubationTask->forceCompletion(); } diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp index bac5cc7582..d075d3b64b 100644 --- a/src/quick/items/qquickwindowmanager.cpp +++ b/src/quick/items/qquickwindowmanager.cpp @@ -1221,7 +1221,7 @@ void QQuickTrivialWindowManager::renderCanvas(QQuickCanvas *canvas) maybeUpdate(canvas); } -void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *canvas) +void QQuickTrivialWindowManager::exposureChanged(QQuickCanvas *) { } diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h index 72fd2b32bd..156ccec127 100644 --- a/src/quick/items/qquickwindowmodule_p.h +++ b/src/quick/items/qquickwindowmodule_p.h @@ -42,14 +42,14 @@ #ifndef QQUICKWINDOWMODULE_H #define QQUICKWINDOWMODULE_H -#include <qqml.h> +#include <private/qtquickglobal_p.h> QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QQuickWindowModule +class Q_QUICK_PRIVATE_EXPORT QQuickWindowModule { public: static void defineModule(); diff --git a/src/quick/particles/qquickcustomparticle.cpp b/src/quick/particles/qquickcustomparticle.cpp index 8f75672e32..7d27e48c6b 100644 --- a/src/quick/particles/qquickcustomparticle.cpp +++ b/src/quick/particles/qquickcustomparticle.cpp @@ -130,29 +130,27 @@ struct PlainVertices { QQuickCustomParticle::QQuickCustomParticle(QQuickItem* parent) : QQuickParticlePainter(parent) - , m_dirtyData(true) - , m_material(0) - , m_rootNode(0) + , m_dirtyUniforms(true) + , m_dirtyUniformValues(true) + , m_dirtyTextureProviders(true) + , m_dirtyProgram(true) { setFlag(QQuickItem::ItemHasContents); } -class QQuickShaderEffectMaterialObject : public QObject, public QQuickShaderEffectMaterial { }; - void QQuickCustomParticle::sceneGraphInvalidated() { m_nodes.clear(); - m_rootNode = 0; } QQuickCustomParticle::~QQuickCustomParticle() { - if (m_material) - m_material->deleteLater(); } void QQuickCustomParticle::componentComplete() { + m_common.updateShader(this, Key::FragmentShader); + updateVertexShader(); reset(); QQuickParticlePainter::componentComplete(); } @@ -170,10 +168,12 @@ void QQuickCustomParticle::componentComplete() void QQuickCustomParticle::setFragmentShader(const QByteArray &code) { - if (m_source.fragmentCode.constData() == code.constData()) + if (m_common.source.sourceCode[Key::FragmentShader].constData() == code.constData()) return; - m_source.fragmentCode = code; + m_common.source.sourceCode[Key::FragmentShader] = code; + m_dirtyProgram = true; if (isComponentComplete()) { + m_common.updateShader(this, Key::FragmentShader); reset(); } emit fragmentShaderChanged(); @@ -222,236 +222,108 @@ void QQuickCustomParticle::setFragmentShader(const QByteArray &code) void QQuickCustomParticle::setVertexShader(const QByteArray &code) { - if (m_source.vertexCode.constData() == code.constData()) + if (m_common.source.sourceCode[Key::VertexShader].constData() == code.constData()) return; - m_source.vertexCode = code; + m_common.source.sourceCode[Key::VertexShader] = code; + + m_dirtyProgram = true; if (isComponentComplete()) { + updateVertexShader(); reset(); } emit vertexShaderChanged(); } -void QQuickCustomParticle::reset() +void QQuickCustomParticle::updateVertexShader() { - disconnectPropertySignals(); - - m_source.attributeNames.clear(); - m_source.uniformNames.clear(); - m_source.respectsOpacity = false; - m_source.respectsMatrix = false; - m_source.className = metaObject()->className(); - - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - delete source.mapper; - if (source.item && source.item->parentItem() == this) - source.item->setParentItem(0); - } - m_sources.clear(); - - QQuickParticlePainter::reset(); - m_pleaseReset = true; - update(); + m_common.disconnectPropertySignals(this, Key::VertexShader); + qDeleteAll(m_common.signalMappers[Key::VertexShader]); + m_common.uniformData[Key::VertexShader].clear(); + m_common.signalMappers[Key::VertexShader].clear(); + m_common.attributes.clear(); + m_common.attributes.append("qt_ParticlePos"); + m_common.attributes.append("qt_ParticleTex"); + m_common.attributes.append("qt_ParticleData"); + m_common.attributes.append("qt_ParticleVec"); + m_common.attributes.append("qt_ParticleR"); + + UniformData d; + d.name = "qt_Matrix"; + d.specialType = UniformData::Matrix; + m_common.uniformData[Key::VertexShader].append(d); + m_common.signalMappers[Key::VertexShader].append(0); + + d.name = "qt_Timestamp"; + d.specialType = UniformData::None; + m_common.uniformData[Key::VertexShader].append(d); + m_common.signalMappers[Key::VertexShader].append(0); + + const QByteArray &code = m_common.source.sourceCode[Key::VertexShader]; + if (!code.isEmpty()) + m_common.lookThroughShaderCode(this, Key::VertexShader, code); + + m_common.connectPropertySignals(this, Key::VertexShader); } - -void QQuickCustomParticle::changeSource(int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - QVariant v = property(m_sources.at(index).name.constData()); - setSource(v, index); -} - -void QQuickCustomParticle::updateData() +void QQuickCustomParticle::reset() { - m_dirtyData = true; + QQuickParticlePainter::reset(); update(); } -void QQuickCustomParticle::setSource(const QVariant &var, int index) -{ - Q_ASSERT(index >= 0 && index < m_sources.size()); - - SourceData &source = m_sources[index]; - - source.item = 0; - if (var.isNull()) { - return; - } else if (!qVariantCanConvert<QObject *>(var)) { - qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData()); - return; - } - - QObject *obj = qVariantValue<QObject *>(var); - source.item = qobject_cast<QQuickItem *>(obj); - if (!source.item || !source.item->isTextureProvider()) { - qWarning("ShaderEffect: source uniform [%s] is not assigned a valid texture provider: %s [%s]", - source.name.constData(), qPrintable(obj->objectName()), obj->metaObject()->className()); - return; - } - - // TODO: Copy better solution in QQuickShaderEffect when they find it. - // 'source.item' needs a canvas to get a scenegraph node. - // The easiest way to make sure it gets a canvas is to - // make it a part of the same item tree as 'this'. - if (source.item && source.item->parentItem() == 0) { - source.item->setParentItem(this); - source.item->setVisible(false); - } -} - -void QQuickCustomParticle::disconnectPropertySignals() -{ - disconnect(this, 0, this, SLOT(updateData())); - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - disconnect(this, 0, source.mapper, 0); - disconnect(source.mapper, 0, this, 0); - } -} - -void QQuickCustomParticle::connectPropertySignals() -{ - QSet<QByteArray>::const_iterator it; - for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) { - int pi = metaObject()->indexOfProperty(it->constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - if (!mp.hasNotifySignal()) - qWarning("QQuickCustomParticle: property '%s' does not have notification method!", it->constData()); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().methodSignature()); - connect(this, signalName, this, SLOT(updateData())); - } else { - qWarning("QQuickCustomParticle: '%s' does not have a matching property!", it->constData()); - } - } - for (int i = 0; i < m_sources.size(); ++i) { - SourceData &source = m_sources[i]; - int pi = metaObject()->indexOfProperty(source.name.constData()); - if (pi >= 0) { - QMetaProperty mp = metaObject()->property(pi); - QByteArray signalName("2"); - signalName.append(mp.notifySignal().methodSignature()); - connect(this, signalName, source.mapper, SLOT(map())); - source.mapper->setMapping(this, i); - connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int))); - } else { - qWarning("QQuickCustomParticle: '%s' does not have a matching source!", source.name.constData()); - } - } -} - -void QQuickCustomParticle::updateProperties() -{ - QByteArray vertexCode = m_source.vertexCode; - QByteArray fragmentCode = m_source.fragmentCode; - if (vertexCode.isEmpty()) - vertexCode = qt_particles_default_vertex_code; - if (fragmentCode.isEmpty()) - fragmentCode = qt_particles_default_fragment_code; - vertexCode = qt_particles_template_vertex_code + vertexCode; - - m_source.attributeNames.clear(); - m_source.attributeNames << "qt_ParticlePos" - << "qt_ParticleTex" - << "qt_ParticleData" - << "qt_ParticleVec" - << "qt_ParticleR"; - - lookThroughShaderCode(vertexCode); - lookThroughShaderCode(fragmentCode); - - if (!m_source.respectsMatrix) - qWarning("QQuickCustomParticle: Missing reference to \'qt_Matrix\'."); - if (!m_source.respectsOpacity) - qWarning("QQuickCustomParticle: Missing reference to \'qt_Opacity\'."); - - for (int i = 0; i < m_sources.size(); ++i) { - QVariant v = property(m_sources.at(i).name); - setSource(v, i); - } - - connectPropertySignals(); -} - -void QQuickCustomParticle::lookThroughShaderCode(const QByteArray &code) -{ - // Regexp for matching attributes and uniforms. - // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name> - static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); - Q_ASSERT(re.isValid()); - - int pos = -1; - - QString wideCode = QString::fromLatin1(code.constData(), code.size()); - - while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { - QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute - QByteArray type = re.cap(2).toLatin1(); // type - QByteArray name = re.cap(3).toLatin1(); // variable name - - if (decl == "attribute") { - if (!m_source.attributeNames.contains(name)) - qWarning() << "Custom Particle: Unknown attribute " << name; - } else { - Q_ASSERT(decl == "uniform");//TODO: Shouldn't assert - - if (name == "qt_Matrix") { - m_source.respectsMatrix = true; - } else if (name == "qt_Opacity") { - m_source.respectsOpacity = true; - } else if (name == "qt_Timestamp") { - //Not strictly necessary - } else { - m_source.uniformNames.insert(name); - if (type == "sampler2D") { - SourceData d; - d.mapper = new QSignalMapper; - d.name = name; - d.item = 0; - m_sources.append(d); - } - } - } - } -} - QSGNode *QQuickCustomParticle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { - Q_UNUSED(oldNode); + QQuickShaderEffectNode *rootNode = static_cast<QQuickShaderEffectNode *>(oldNode); if (m_pleaseReset){ - - //delete m_material;//Shader effect item doesn't regen material? - - delete m_rootNode;//Automatically deletes children - m_rootNode = 0; + delete rootNode;//Automatically deletes children + rootNode = 0; m_nodes.clear(); m_pleaseReset = false; - m_dirtyData = false; + m_dirtyProgram = true; } if (m_system && m_system->isRunning() && !m_system->isPaused()){ - prepareNextFrame(); - if (m_rootNode) { + rootNode = prepareNextFrame(rootNode); + if (rootNode) update(); - foreach (QSGGeometryNode* node, m_nodes) - node->markDirty(QSGNode::DirtyGeometry);//done in buildData? - } } - return m_rootNode; + return rootNode; } -void QQuickCustomParticle::prepareNextFrame(){ - if (!m_rootNode) - m_rootNode = buildCustomNodes(); - if (!m_rootNode) - return; +QQuickShaderEffectNode *QQuickCustomParticle::prepareNextFrame(QQuickShaderEffectNode *rootNode) +{ + if (!rootNode) + rootNode = buildCustomNodes(); + + if (!rootNode) + return 0; + + if (m_dirtyProgram) { + QQuickShaderEffectMaterial *material = static_cast<QQuickShaderEffectMaterial *>(rootNode->material()); + Q_ASSERT(material); + + Key s = m_common.source; + if (s.sourceCode[Key::FragmentShader].isEmpty()) + s.sourceCode[Key::FragmentShader] = qt_particles_default_fragment_code; + if (s.sourceCode[Key::VertexShader].isEmpty()) + s.sourceCode[Key::VertexShader] = qt_particles_default_vertex_code; + s.sourceCode[Key::VertexShader] = qt_particles_template_vertex_code + s.sourceCode[Key::VertexShader]; + s.className = metaObject()->className(); + + material->setProgramSource(s); + material->attributes = m_common.attributes; + foreach (QQuickShaderEffectNode* node, m_nodes) + node->markDirty(QSGNode::DirtyMaterial); + + m_dirtyProgram = false; + m_dirtyUniforms = true; + } m_lastTime = m_system->systemSync(this) / 1000.; - if (m_dirtyData || true)//Currently this is how we update timestamp... potentially over expensive. - buildData(); + if (true) //Currently this is how we update timestamp... potentially over expensive. + buildData(rootNode); + return rootNode; } QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() @@ -468,20 +340,13 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() return 0; } - updateProperties(); - - QQuickShaderEffectProgram s = m_source; - if (s.fragmentCode.isEmpty()) - s.fragmentCode = qt_particles_default_fragment_code; - if (s.vertexCode.isEmpty()) - s.vertexCode = qt_particles_default_vertex_code; + if (m_groups.isEmpty()) + return 0; - if (!m_material) { - m_material = new QQuickShaderEffectMaterialObject; - } + QQuickShaderEffectNode *rootNode = 0; + QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial; + m_dirtyProgram = true; - s.vertexCode = qt_particles_template_vertex_code + s.vertexCode; - m_material->setProgramSource(s); foreach (const QString &str, m_groups){ int gIdx = m_system->groupIds[str]; int count = m_system->groupData[gIdx]->size(); @@ -489,8 +354,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() QQuickShaderEffectNode* node = new QQuickShaderEffectNode(); m_nodes.insert(gIdx, node); - node->setMaterial(m_material); - node->markDirty(QSGNode::DirtyMaterial); + node->setMaterial(material); //Create Particle Geometry int vCount = count * 4; @@ -498,6 +362,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() QSGGeometry *g = new QSGGeometry(PlainParticle_AttributeSet, vCount, iCount); g->setDrawingMode(GL_TRIANGLES); node->setGeometry(g); + node->setFlag(QSGNode::OwnsGeometry, true); PlainVertex *vertices = (PlainVertex *) g->vertexData(); for (int p=0; p < count; ++p) { commit(gIdx, p); @@ -526,48 +391,46 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() indices += 6; } } - foreach (QQuickShaderEffectNode* node, m_nodes){ - if (node == *(m_nodes.begin())) - continue; - (*(m_nodes.begin()))->appendChildNode(node); - } - return *(m_nodes.begin()); + QHash<int, QQuickShaderEffectNode*>::const_iterator it = m_nodes.begin(); + rootNode = it.value(); + rootNode->setFlag(QSGNode::OwnsMaterial, true); + for (++it; it != m_nodes.end(); ++it) + rootNode->appendChildNode(it.value()); + + return rootNode; } +void QQuickCustomParticle::sourceDestroyed(QObject *object) +{ + m_common.sourceDestroyed(object); +} -void QQuickCustomParticle::buildData() +void QQuickCustomParticle::propertyChanged(int mappedId) { - if (!m_rootNode) + bool textureProviderChanged; + m_common.propertyChanged(this, mappedId, &textureProviderChanged); + m_dirtyTextureProviders |= textureProviderChanged; + m_dirtyUniformValues = true; + update(); +} + + +void QQuickCustomParticle::buildData(QQuickShaderEffectNode *rootNode) +{ + if (!rootNode) return; - const QByteArray timestampName("qt_Timestamp"); - QVector<QPair<QByteArray, QVariant> > values; - QVector<QPair<QByteArray, QSGTextureProvider *> > textures; - const QVector<QPair<QByteArray, QSGTextureProvider *> > &oldTextures = m_material->textureProviders(); - for (int i = 0; i < oldTextures.size(); ++i) { - QSGTextureProvider *t = oldTextures.at(i).second; - if (t) - foreach (QQuickShaderEffectNode* node, m_nodes) - disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture())); - } - for (int i = 0; i < m_sources.size(); ++i) { - const SourceData &source = m_sources.at(i); - QSGTextureProvider *t = source.item->textureProvider(); - textures.append(qMakePair(source.name, t)); - if (t) - foreach (QQuickShaderEffectNode* node, m_nodes) - connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection); - } - for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin(); - it != m_source.uniformNames.end(); ++it) { - values.append(qMakePair(*it, property(*it))); + for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) { + for (int i = 0; i < m_common.uniformData[shaderType].size(); ++i) { + if (m_common.uniformData[shaderType].at(i).name == "qt_Timestamp") + m_common.uniformData[shaderType][i].value = qVariantFromValue(m_lastTime); + } } - values.append(qMakePair(timestampName, QVariant(m_lastTime))); - m_material->setUniforms(values); - m_material->setTextureProviders(textures); - m_dirtyData = false; + m_common.updateMaterial(rootNode, static_cast<QQuickShaderEffectMaterial *>(rootNode->material()), + m_dirtyUniforms, true, m_dirtyTextureProviders); foreach (QQuickShaderEffectNode* node, m_nodes) node->markDirty(QSGNode::DirtyMaterial); + m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; } void QQuickCustomParticle::initialize(int gIdx, int pIdx) @@ -599,4 +462,12 @@ void QQuickCustomParticle::commit(int gIdx, int pIdx) } } +void QQuickCustomParticle::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == QQuickItem::ItemSceneChange) + m_common.updateCanvas(value.canvas); + QQuickParticlePainter::itemChange(change, value); +} + + QT_END_NAMESPACE diff --git a/src/quick/particles/qquickcustomparticle_p.h b/src/quick/particles/qquickcustomparticle_p.h index e04ac704d0..f689091268 100644 --- a/src/quick/particles/qquickcustomparticle_p.h +++ b/src/quick/particles/qquickcustomparticle_p.h @@ -43,6 +43,7 @@ #define CUSTOM_PARTICLE_H #include "qquickparticlepainter_p.h" #include <private/qquickshadereffectnode_p.h> +#include <private/qquickshadereffect_p.h> #include <QSignalMapper> QT_BEGIN_HEADER @@ -65,53 +66,50 @@ public: explicit QQuickCustomParticle(QQuickItem* parent=0); ~QQuickCustomParticle(); - QByteArray fragmentShader() const { return m_source.fragmentCode; } + QByteArray fragmentShader() const { return m_common.source.sourceCode[Key::FragmentShader]; } void setFragmentShader(const QByteArray &code); - QByteArray vertexShader() const { return m_source.vertexCode; } + QByteArray vertexShader() const { return m_common.source.sourceCode[Key::VertexShader]; } void setVertexShader(const QByteArray &code); -public Q_SLOTS: - void updateData(); - void changeSource(int); + Q_SIGNALS: void fragmentShaderChanged(); void vertexShaderChanged(); + protected: virtual void initialize(int gIdx, int pIdx); virtual void commit(int gIdx, int pIdx); QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); - void prepareNextFrame(); - void setSource(const QVariant &var, int index); - void disconnectPropertySignals(); - void connectPropertySignals(); + QQuickShaderEffectNode *prepareNextFrame(QQuickShaderEffectNode *rootNode); void reset(); void resize(int oldCount, int newCount); - void updateProperties(); - void lookThroughShaderCode(const QByteArray &code); virtual void componentComplete(); QQuickShaderEffectNode *buildCustomNodes(); - void performPendingResize(); void sceneGraphInvalidated(); + void itemChange(ItemChange change, const ItemChangeData &value); + +private Q_SLOTS: + void sourceDestroyed(QObject *object); + void propertyChanged(int mappedId); private: - void buildData(); - - bool m_dirtyData; - QQuickShaderEffectProgram m_source; - struct SourceData - { - QSignalMapper *mapper; - QPointer<QQuickItem> item; - QByteArray name; - }; - QVector<SourceData> m_sources; - QQuickShaderEffectMaterialObject *m_material; - QQuickShaderEffectNode* m_rootNode; + typedef QQuickShaderEffectMaterialKey Key; + typedef QQuickShaderEffectMaterial::UniformData UniformData; + + void buildData(QQuickShaderEffectNode *rootNode); + void updateVertexShader(); + + QQuickShaderEffectCommon m_common; + QHash<int, QQuickShaderEffectNode*> m_nodes; qreal m_lastTime; + uint m_dirtyUniforms : 1; + uint m_dirtyUniformValues : 1; + uint m_dirtyTextureProviders : 1; + uint m_dirtyProgram : 1; }; QT_END_NAMESPACE diff --git a/src/quick/particles/qquickparticleemitter_p.h b/src/quick/particles/qquickparticleemitter_p.h index 70adcff34c..eb9e1fd591 100644 --- a/src/quick/particles/qquickparticleemitter_p.h +++ b/src/quick/particles/qquickparticleemitter_p.h @@ -178,7 +178,8 @@ public slots: { if (m_system != arg) { m_system = arg; - m_system->registerParticleEmitter(this); + if (m_system) + m_system->registerParticleEmitter(this); emit systemChanged(arg); } } diff --git a/src/quick/particles/qquickparticlesmodule_p.h b/src/quick/particles/qquickparticlesmodule_p.h index b7cf09919e..23a40488cf 100644 --- a/src/quick/particles/qquickparticlesmodule_p.h +++ b/src/quick/particles/qquickparticlesmodule_p.h @@ -39,16 +39,16 @@ ** ****************************************************************************/ -#ifndef QQuickPARTICLESMODULE_H -#define QQuickPARTICLESMODULE_H +#ifndef QQUICKPARTICLESMODULE_H +#define QQUICKPARTICLESMODULE_H -#include <qqml.h> +#include <private/qtquickglobal_p.h> QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QQuickParticlesModule +class Q_QUICK_PRIVATE_EXPORT QQuickParticlesModule { public: static void defineModule(); @@ -58,4 +58,4 @@ QT_END_NAMESPACE QT_END_HEADER -#endif // QQuickPARTICLESMODULE_H +#endif // QQUICKPARTICLESMODULE_H diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index 16cf2198a2..8a0c05618a 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -44,14 +44,12 @@ #include <private/qquickutilmodule_p.h> #include <private/qquickvaluetypes_p.h> #include <private/qquickitemsmodule_p.h> -#include <private/qquickparticlesmodule_p.h> -#include <private/qquickwindowmodule_p.h> -#include <private/qquickapplication_p.h> #include <private/qqmlenginedebugservice_p.h> #include <private/qqmldebugstatesdelegate_p.h> #include <private/qqmlbinding_p.h> #include <private/qqmlcontext_p.h> +#include <private/qquickapplication_p.h> #include <QtQuick/private/qquickpropertychanges_p.h> #include <QtQuick/private/qquickstate_p.h> #include <qqmlproperty.h> @@ -176,8 +174,6 @@ void QQmlQtQuick2Module::defineModule() QQuickUtilModule::defineModule(); QQmlEnginePrivate::defineModule(); QQuickItemsModule::defineModule(); - QQuickParticlesModule::defineModule(); - QQuickWindowModule::defineModule(); qmlRegisterUncreatableType<QQuickApplication>("QtQuick",2,0,"Application", QQuickApplication::tr("Application is an abstract class")); diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp index cd5aaaedd7..dd9db4e904 100644 --- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache.cpp @@ -86,11 +86,11 @@ QSGSharedDistanceFieldGlyphCache::QSGSharedDistanceFieldGlyphCache(const QByteAr connect(sharedGraphicsCache, SIGNAL(itemsMissing(QByteArray,QVector<quint32>)), this, SLOT(reportItemsMissing(QByteArray,QVector<quint32>)), Qt::DirectConnection); - connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)), - this, SLOT(reportItemsAvailable(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)), + connect(sharedGraphicsCache, SIGNAL(itemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), + this, SLOT(reportItemsAvailable(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), Qt::DirectConnection); - connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)), - this, SLOT(reportItemsUpdated(QByteArray,void*,QSize,QVector<quint32>,QVector<QPoint>)), + connect(sharedGraphicsCache, SIGNAL(itemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), + this, SLOT(reportItemsUpdated(QByteArray,void*,QVector<quint32>,QVector<QPoint>)), Qt::DirectConnection); connect(sharedGraphicsCache, SIGNAL(itemsInvalidated(QByteArray,QVector<quint32>)), this, SLOT(reportItemsInvalidated(QByteArray,QVector<quint32>)), @@ -537,7 +537,7 @@ void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs() while (it != textureContentForBuffer.constEnd()) { Texture texture; texture.textureId = m_sharedGraphicsCache->textureIdForBuffer(it.key()); - texture.size = it.value().size; + texture.size = m_sharedGraphicsCache->sizeOfBuffer(it.key()); #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG_) saveTexture(texture.textureId, texture.size.width(), texture.size.height()); @@ -557,7 +557,6 @@ void QSGSharedDistanceFieldGlyphCache::processPendingGlyphs() void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &cacheId, void *bufferId, - const QSize &bufferSize, const QVector<quint32> &itemIds, const QVector<QPoint> &positions) { @@ -568,8 +567,8 @@ void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &ca return; #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs, bufferSize: %dx%d)", - cacheId.constData(), itemIds.size(), bufferSize.width(), bufferSize.height()); + qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsAvailable() called for %s (%d glyphs)", + cacheId.constData(), itemIds.size()); #endif for (int i=0; i<itemIds.size(); ++i) { @@ -581,11 +580,11 @@ void QSGSharedDistanceFieldGlyphCache::reportItemsAvailable(const QByteArray &ca } if (requestedItemsInList) - reportItemsUpdated(cacheId, bufferId, bufferSize, itemIds, positions); + reportItemsUpdated(cacheId, bufferId,itemIds, positions); } void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cacheId, - void *bufferId, const QSize &bufferSize, + void *bufferId, const QVector<quint32> &itemIds, const QVector<QPoint> &positions) { @@ -597,19 +596,17 @@ void QSGSharedDistanceFieldGlyphCache::reportItemsUpdated(const QByteArray &cach Q_ASSERT(itemIds.size() == positions.size()); #if defined(QSGSHAREDDISTANCEFIELDGLYPHCACHE_DEBUG) - qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs, bufferSize: %dx%d)", - cacheId.constData(), itemIds.size(), bufferSize.width(), bufferSize.height()); + qDebug("QSGSharedDistanceFieldGlyphCache::reportItemsUpdated() called for %s (%d glyphs)", + cacheId.constData(), itemIds.size()); #endif for (int i=0; i<itemIds.size(); ++i) { if (m_requestedGlyphs.contains(itemIds.at(i))) { PendingGlyph &pendingGlyph = m_pendingReadyGlyphs[itemIds.at(i)]; void *oldBuffer = pendingGlyph.buffer; - Q_ASSERT(bufferSize.height() >= pendingGlyph.bufferSize.height()); pendingGlyph.buffer = bufferId; pendingGlyph.position = positions.at(i); - pendingGlyph.bufferSize = bufferSize; m_sharedGraphicsCache->referenceBuffer(bufferId); if (oldBuffer != 0) diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h index 19844bbda4..2d43246bb0 100644 --- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h @@ -77,10 +77,13 @@ Q_SIGNALS: private Q_SLOTS: void reportItemsMissing(const QByteArray &cacheId, const QVector<quint32> &itemIds); void reportItemsAvailable(const QByteArray &cacheId, - void *bufferId, const QSize &bufferSize, - const QVector<quint32> &itemIds, const QVector<QPoint> &positions); - void reportItemsUpdated(const QByteArray &cacheId, void *bufferId, const QSize &bufferSize, - const QVector<quint32> &itemIds, const QVector<QPoint> &positions); + void *bufferId, + const QVector<quint32> &itemIds, + const QVector<QPoint> &positions); + void reportItemsUpdated(const QByteArray &cacheId, + void *bufferId, + const QVector<quint32> &itemIds, + const QVector<QPoint> &positions); void reportItemsInvalidated(const QByteArray &cacheId, const QVector<quint32> &itemIds); private: diff --git a/src/quick/scenegraph/util/qsgpainternode.cpp b/src/quick/scenegraph/util/qsgpainternode.cpp index 1ea64f6205..87a54d3124 100644 --- a/src/quick/scenegraph/util/qsgpainternode.cpp +++ b/src/quick/scenegraph/util/qsgpainternode.cpp @@ -73,6 +73,10 @@ QSGPainterTexture::QSGPainterTexture() } +#ifdef QT_OPENGL_ES +extern void qsg_swizzleBGRAToRGBA(QImage *image); +#endif + void QSGPainterTexture::bind() { if (m_dirty_rect.isNull()) { @@ -91,6 +95,7 @@ void QSGPainterTexture::bind() int h = m_dirty_rect.height(); #ifdef QT_OPENGL_ES + qsg_swizzleBGRAToRGBA(&subImage); glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_rect.x(), m_dirty_rect.y(), w, h, GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits()); #else diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 24b92fa98f..7be38ff109 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -402,7 +402,7 @@ QSGPlainTexture::~QSGPlainTexture() } #ifdef QT_OPENGL_ES -static void swizzleBGRAToRGBA(QImage *image) +void qsg_swizzleBGRAToRGBA(QImage *image) { const int width = image->width(); const int height = image->height(); @@ -500,7 +500,7 @@ void QSGPlainTexture::bind() updateBindOptions(m_dirty_bind_options); #ifdef QT_OPENGL_ES - swizzleBGRAToRGBA(&tmp); + qsg_swizzleBGRAToRGBA(&tmp); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits()); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits()); diff --git a/src/quick/util/qquickconnections.cpp b/src/quick/util/qquickconnections.cpp index acc9738f2c..27b66ba38f 100644 --- a/src/quick/util/qquickconnections.cpp +++ b/src/quick/util/qquickconnections.cpp @@ -280,10 +280,9 @@ void QQuickConnections::connectSignals() location = ddata->outerContext->urlString; } - QQmlExpression *expression = ctxtdata ? - QQmlExpressionPrivate::create(ctxtdata, 0, script, true, location, line, column) : 0; + QQmlBoundSignalExpression *expression = ctxtdata ? + new QQmlBoundSignalExpression(ctxtdata, 0, script, true, location, line, column) : 0; signal->setExpression(expression); - signal->addToObject(); d->boundsignals += signal; } else { if (!d->ignoreUnknownSignals) diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index 8b0818c96c..4bff006d9b 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -55,6 +55,7 @@ #include <private/qqmlproperty_p.h> #include <private/qqmlcontext_p.h> #include <private/qquickstate_p_p.h> +#include <private/qqmlboundsignal_p.h> #include <QtCore/qdebug.h> @@ -139,7 +140,7 @@ class QQuickReplaceSignalHandler : public QQuickActionEvent { public: QQuickReplaceSignalHandler() : expression(0), reverseExpression(0), - rewindExpression(0), ownedExpression(0) {} + rewindExpression(0), ownedExpression(0), ownedExpressionWatcher(0) {} ~QQuickReplaceSignalHandler() { delete ownedExpression; } @@ -147,22 +148,35 @@ public: virtual EventType type() const { return SignalHandler; } QQmlProperty property; - QQmlExpression *expression; - QQmlExpression *reverseExpression; - QQmlExpression *rewindExpression; - QQmlGuard<QQmlExpression> ownedExpression; + QQmlBoundSignalExpression *expression; + QQmlBoundSignalExpression *reverseExpression; + QQmlBoundSignalExpression *rewindExpression; + QQmlBoundSignalExpression *ownedExpression; + QQmlAbstractExpression::DeleteWatcher *ownedExpressionWatcher; // TODO: refactor the ownership impl. virtual void execute(Reason) { ownedExpression = QQmlPropertyPrivate::setSignalExpression(property, expression); - if (ownedExpression == expression) + if (ownedExpression == expression) { + delete ownedExpressionWatcher; + ownedExpressionWatcher = 0; ownedExpression = 0; + } else if (ownedExpression) { + delete ownedExpressionWatcher; + ownedExpressionWatcher = new QQmlAbstractExpression::DeleteWatcher(ownedExpression); + } } virtual bool isReversable() { return true; } virtual void reverse(Reason) { ownedExpression = QQmlPropertyPrivate::setSignalExpression(property, reverseExpression); - if (ownedExpression == reverseExpression) + if (ownedExpression == reverseExpression) { + delete ownedExpressionWatcher; + ownedExpressionWatcher = 0; ownedExpression = 0; + } else if (ownedExpression) { + delete ownedExpressionWatcher; + ownedExpressionWatcher = new QQmlAbstractExpression::DeleteWatcher(ownedExpression); + } } virtual void saveOriginals() { @@ -181,6 +195,8 @@ public: if (rsh->ownedExpression == reverseExpression) { ownedExpression = rsh->ownedExpression; rsh->ownedExpression = 0; + delete ownedExpressionWatcher; + ownedExpressionWatcher = new QQmlAbstractExpression::DeleteWatcher(ownedExpression); } } @@ -225,11 +241,17 @@ public: public: ExpressionChange(const QString &_name, QQmlBinding::Identifier _id, - QQmlExpression *_expr) - : name(_name), id(_id), expression(_expr) {} + const QString& _expr, + const QUrl &_url, + int _line, + int _column) + : name(_name), id(_id), expression(_expr), url(_url), line(_line), column(_column) {} QString name; QQmlBinding::Identifier id; - QQmlExpression *expression; + QString expression; + QUrl url; + int line; + int column; }; QList<QPair<QString, QVariant> > properties; @@ -334,20 +356,36 @@ void QQuickPropertyChangesPrivate::decode() QQmlProperty prop = property(name); //### better way to check for signal property? if (prop.type() & QQmlProperty::SignalProperty) { - QQmlExpression *expression = new QQmlExpression(qmlContext(q), object, data.toString()); + QString expression = data.toString(); + QUrl url = QUrl(); + int line = -1; + int column = -1; + QQmlData *ddata = QQmlData::get(q); - if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) - expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber, ddata->columnNumber); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) { + url = ddata->outerContext->url; + line = ddata->lineNumber; + column = ddata->columnNumber; + } + QQuickReplaceSignalHandler *handler = new QQuickReplaceSignalHandler; handler->property = prop; - handler->expression = expression; + handler->expression = new QQmlBoundSignalExpression(QQmlContextData::get(qmlContext(q)), object, expression, false, url.toString(), line, column); signalReplacements << handler; - } else if (isScript) { - QQmlExpression *expression = new QQmlExpression(qmlContext(q), object, data.toString()); + } else if (isScript) { // binding + QString expression = data.toString(); + QUrl url = QUrl(); + int line = -1; + int column = -1; + QQmlData *ddata = QQmlData::get(q); - if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) - expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber, ddata->columnNumber); - expressions << ExpressionChange(name, id, expression); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) { + url = ddata->outerContext->url; + line = ddata->lineNumber; + column = ddata->columnNumber; + } + + expressions << ExpressionChange(name, id, expression, url, line, column); } else { properties << qMakePair(name, data); } @@ -374,8 +412,6 @@ QQuickPropertyChanges::QQuickPropertyChanges() QQuickPropertyChanges::~QQuickPropertyChanges() { Q_D(QQuickPropertyChanges); - for(int ii = 0; ii < d->expressions.count(); ++ii) - delete d->expressions.at(ii).expression; for(int ii = 0; ii < d->signalReplacements.count(); ++ii) delete d->signalReplacements.at(ii); } @@ -460,7 +496,8 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() for (int ii = 0; ii < d->expressions.count(); ++ii) { - const QString &property = d->expressions.at(ii).name; + QQuickPropertyChangesPrivate::ExpressionChange e = d->expressions.at(ii); + const QString &property = e.name; QQmlProperty prop = d->property(property); if (prop.isValid()) { @@ -471,16 +508,18 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() a.specifiedObject = d->object; a.specifiedProperty = property; + QQmlBinding *newBinding = e.id != QQmlBinding::Invalid ? QQmlBinding::createBinding(e.id, object(), qmlContext(this), e.url.toString(), e.column) : 0; + if (!newBinding) + newBinding = new QQmlBinding(e.expression, false, object(), QQmlContextData::get(qmlContext(this)), e.url.toString(), e.line, e.column); + if (d->isExplicit) { - a.toValue = d->expressions.at(ii).expression->evaluate(); + // in this case, we don't want to assign a binding, per se, + // so we evaluate the expression and assign the result. + // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString) + // so that we can avoid creating then destroying the binding in this case. + a.toValue = newBinding->evaluate(); + newBinding->destroy(); } else { - QQmlExpression *e = d->expressions.at(ii).expression; - - QQmlBinding::Identifier id = d->expressions.at(ii).id; - QQmlBinding *newBinding = id != QQmlBinding::Invalid ? QQmlBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0; - if (!newBinding) - newBinding = new QQmlBinding(e->expression(), false, object(), QQmlContextData::get(qmlContext(this)), - e->sourceFile(), e->lineNumber(), e->columnNumber()); newBinding->setTarget(prop); a.toBinding = QQmlAbstractBinding::getPointer(newBinding); a.deletableToBinding = true; @@ -635,14 +674,14 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); while (expressionIterator.hasNext()) { - const ExpressionEntry &entry = expressionIterator.next(); + ExpressionEntry &entry = expressionIterator.next(); if (entry.name == name) { - entry.expression->setExpression(expression); + entry.expression = expression; if (state() && state()->isStateActive()) { QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(d->property(name)); if (oldBinding) { - QQmlPropertyPrivate::setBinding(d->property(name), 0); - oldBinding->destroy(); + QQmlPropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); } QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); @@ -653,8 +692,8 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString } } - QQmlExpression *newExpression = new QQmlExpression(qmlContext(this), d->object, expression); - expressionIterator.insert(ExpressionEntry(name, QQmlBinding::Invalid, newExpression)); + // adding a new expression. + expressionIterator.insert(ExpressionEntry(name, QQmlBinding::Invalid, expression, QUrl(), -1, -1)); if (state() && state()->isStateActive()) { if (hadValue) { @@ -675,11 +714,14 @@ void QQuickPropertyChanges::changeExpression(const QString &name, const QString action.specifiedObject = object(); action.specifiedProperty = name; - + QQmlBinding *newBinding = new QQmlBinding(expression, object(), qmlContext(this)); if (d->isExplicit) { - action.toValue = newExpression->evaluate(); + // don't assign the binding, merely evaluate the expression. + // XXX TODO: add a static QQmlJavaScriptExpression::evaluate(QString) + // so that we can avoid creating then destroying the binding in this case. + action.toValue = newBinding->evaluate(); + newBinding->destroy(); } else { - QQmlBinding *newBinding = new QQmlBinding(newExpression->expression(), object(), qmlContext(this)); newBinding->setTarget(d->property(name)); action.toBinding = QQmlAbstractBinding::getPointer(newBinding); action.deletableToBinding = true; @@ -714,7 +756,7 @@ QVariant QQuickPropertyChanges::property(const QString &name) const while (expressionIterator.hasNext()) { const ExpressionEntry &entry = expressionIterator.next(); if (entry.name == name) { - return QVariant(entry.expression->expression()); + return QVariant(entry.expression); } } @@ -773,7 +815,7 @@ QString QQuickPropertyChanges::expression(const QString &name) const while (expressionIterator.hasNext()) { const ExpressionEntry &entry = expressionIterator.next(); if (entry.name == name) { - return entry.expression->expression(); + return entry.expression; } } diff --git a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp index 2d52ea9f50..036641cdfc 100644 --- a/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp +++ b/tests/auto/qml/debugger/qqmldebugclient/tst_qqmldebugclient.cpp @@ -81,7 +81,6 @@ void tst_QQmlDebugClient::initTestCase() QQmlDebugTestClient client("tst_QQmlDebugClient::handshake()", m_conn); QQmlDebugTestService service("tst_QQmlDebugClient::handshake()"); - QTest::ignoreMessage(QtDebugMsg, "QML Debugger: Connection established."); for (int i = 0; i < 50; ++i) { // try for 5 seconds ... m_conn->connectToHost("127.0.0.1", PORT); @@ -173,7 +172,6 @@ void tst_QQmlDebugClient::sequentialConnect() QTest::qWait(100); connection2.connectToHost("127.0.0.1", PORT); - QTest::ignoreMessage(QtDebugMsg, "QML Debugger: Connection established."); QVERIFY(connection2.waitForConnected()); QVERIFY(connection2.isConnected()); QTRY_VERIFY(client2.state() == QQmlDebugClient::Enabled); diff --git a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp index 8eb1523af9..12acb9feae 100644 --- a/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp +++ b/tests/auto/qml/debugger/qqmldebugservice/tst_qqmldebugservice.cpp @@ -81,8 +81,6 @@ void tst_QQmlDebugService::initTestCase() m_conn = new QQmlDebugConnection(this); - - QTest::ignoreMessage(QtDebugMsg, "QML Debugger: Connection established."); for (int i = 0; i < 50; ++i) { // try for 5 seconds ... m_conn->connectToHost("127.0.0.1", PORT); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugclient.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugclient.cpp index 9639a36065..0c5dfddffa 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugclient.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/qqmlenginedebugclient.cpp @@ -50,12 +50,14 @@ struct QmlObjectData { QString objectType; int objectId; int contextId; + int parentId; }; QDataStream &operator>>(QDataStream &ds, QmlObjectData &data) { ds >> data.url >> data.lineNumber >> data.columnNumber >> data.idString - >> data.objectName >> data.objectType >> data.objectId >> data.contextId; + >> data.objectName >> data.objectType >> data.objectId >> data.contextId + >> data.parentId; return ds; } diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index b66ba289ce..cc1193b0f1 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -205,7 +205,7 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( // signal properties are fake - they are generated from QQmlAbstractBoundSignal children if (p.name.startsWith("on") && p.name.length() > 2 && p.name[2].isUpper()) { QString signal = p.value.toString(); - QQmlExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name)); + QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name)); QVERIFY(expr && expr->expression() == signal); QVERIFY(p.valueTypeName.isEmpty()); QVERIFY(p.binding.isEmpty()); @@ -328,7 +328,6 @@ void tst_QQmlEngineDebugService::initTestCase() m_conn = new QQmlDebugConnection(this); m_conn->connectToHost("127.0.0.1", 3768); - QTest::ignoreMessage(QtDebugMsg, "QML Debugger: Connection established."); bool ok = m_conn->waitForConnected(); QVERIFY(ok); QTRY_VERIFY(QQmlDebugService::hasDebuggingClient()); diff --git a/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp b/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp index 497d544390..2961cedd51 100644 --- a/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp +++ b/tests/auto/qml/debugger/qv8profilerservice/tst_qv8profilerservice.cpp @@ -77,8 +77,11 @@ public: V8MaximumMessage }; + enum ServiceState { NotRunning, Running } serviceState; + QV8ProfilerClient(QQmlDebugConnection *connection) : QQmlDebugClient(QLatin1String("V8Profiler"), connection) + , serviceState(NotRunning) { } @@ -165,12 +168,15 @@ void QV8ProfilerClient::messageReceived(const QByteArray &message) switch (messageType) { case QV8ProfilerClient::V8Entry: { + QCOMPARE(serviceState, Running); QV8ProfilerData entry; stream >> entry.filename >> entry.functionname >> entry.lineNumber >> entry.totalTime >> entry.selfTime >> entry.treeLevel; traceMessages.append(entry); break; } case QV8ProfilerClient::V8Complete: + QCOMPARE(serviceState, Running); + serviceState = NotRunning; emit complete(); break; case QV8ProfilerClient::V8SnapshotChunk: { @@ -183,6 +189,8 @@ void QV8ProfilerClient::messageReceived(const QByteArray &message) emit snapshot(); break; case QV8ProfilerClient::V8Started: + QCOMPARE(serviceState, NotRunning); + serviceState = Running; emit started(); break; default: @@ -208,15 +216,16 @@ void tst_QV8ProfilerService::connect(bool block, const QString &testFile) m_process = new QQmlDebugProcess(executable); m_process->start(QStringList() << arguments); if (!m_process->waitForSessionStart()) { - QString failMsg = QString("Could not launch app '%1'.\nApplication output:\n%2").arg( - executable, m_process->output()); + QString failMsg = QString("Could not launch app '%1'.").arg(executable); QFAIL(qPrintable(failMsg)); } - QQmlDebugConnection *m_connection = new QQmlDebugConnection(); + m_connection = new QQmlDebugConnection(); m_client = new QV8ProfilerClient(m_connection); m_connection->connectToHost(QLatin1String("127.0.0.1"), PORT); + if (!m_connection->waitForConnected()) + QFAIL("Could not connect to debugger port."); } void tst_QV8ProfilerService::cleanup() @@ -226,7 +235,6 @@ void tst_QV8ProfilerService::cleanup() delete m_process; delete m_connection; - delete m_client; } void tst_QV8ProfilerService::blockingConnectWithTraceEnabled() @@ -235,8 +243,6 @@ void tst_QV8ProfilerService::blockingConnectWithTraceEnabled() QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); m_client->startProfiling(""); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), - "No start signal received in time."); m_client->stopProfiling(""); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); @@ -254,8 +260,6 @@ void tst_QV8ProfilerService::blockingConnectWithTraceDisabled() QFAIL(qPrintable(failMsg)); } m_client->startProfiling(""); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), - "No start signal received in time."); m_client->stopProfiling(""); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); @@ -267,8 +271,6 @@ void tst_QV8ProfilerService::nonBlockingConnect() QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); m_client->startProfiling(""); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), - "No start signal received in time."); m_client->stopProfiling(""); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); @@ -290,9 +292,6 @@ void tst_QV8ProfilerService::profileOnExit() QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled); m_client->startProfiling(""); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), - "No start signal received in time."); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); //QVERIFY(!m_client->traceMessages.isEmpty()); @@ -305,8 +304,6 @@ void tst_QV8ProfilerService::console() m_client->stopProfiling(""); - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(started())), - "No start signal received in time."); QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(complete())), "No trace received in time."); QVERIFY(!m_client->traceMessages.isEmpty()); diff --git a/tests/auto/qml/debugger/shared/debugutil.cpp b/tests/auto/qml/debugger/shared/debugutil.cpp index ac9fa5fae2..925f5372cd 100644 --- a/tests/auto/qml/debugger/shared/debugutil.cpp +++ b/tests/auto/qml/debugger/shared/debugutil.cpp @@ -123,7 +123,7 @@ bool QQmlDebugProcess::waitForSessionStart() qWarning() << "Could not start up " << m_executable; return false; } - m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); + m_eventLoop.exec(); return m_started; } @@ -161,9 +161,6 @@ void QQmlDebugProcess::processAppOutput() m_eventLoop.quit(); continue; } - if (line.contains("Connection established.")) { - continue; - } } } m_mutex.unlock(); diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 3be7919404..6f0b2e45a2 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -158,6 +158,10 @@ with a lower case letter. void tst_qmlmin::qmlMinify_data() { +#if defined(QTEST_CROSS_COMPILED) + return; +#endif + QTest::addColumn<QString>("file"); QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; @@ -173,11 +177,10 @@ void tst_qmlmin::qmlMinify_data() void tst_qmlmin::qmlMinify() { - QFETCH(QString, file); - #if defined(QTEST_CROSS_COMPILED) QSKIP("sources not available when cross compiled"); #endif + QFETCH(QString, file); QProcess qmlminify; qmlminify.start(qmlminPath, QStringList() << QLatin1String("--verify-only") << file); diff --git a/tests/auto/qml/qqmlecmascript/data/InnerObject.qml b/tests/auto/qml/qqmlecmascript/data/InnerObject.qml new file mode 100644 index 0000000000..a8ed9593d1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/InnerObject.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +QtObject { + property int foo1: 100 + property int foo2: 100 + property int foo3: { return 100; } + property int foo4: { return 100; } + + property string bar1: 'Hello' + property string bar2: 'Hello' + property string bar3: { return 'Hello'; } + property string bar4: { return 'Hello'; } +} diff --git a/tests/auto/qml/qqmlecmascript/data/OuterObject.qml b/tests/auto/qml/qqmlecmascript/data/OuterObject.qml new file mode 100644 index 0000000000..da571a9732 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/OuterObject.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + property InnerObject inner: InnerObject {} +} diff --git a/tests/auto/qml/qqmlecmascript/data/deleteLaterObjectMethodCall.qml b/tests/auto/qml/qqmlecmascript/data/deleteLaterObjectMethodCall.qml new file mode 100644 index 0000000000..d1418e57a1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/deleteLaterObjectMethodCall.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +QtObject { + property var fn + + property var c: Component { + MyQmlObject { + function go() { + try { methodNoArgs(); } catch(e) { } + } + } + } + + Component.onCompleted: { + var f = c.createObject().go; + + gc(); + + f(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/ownershipQmlIncubated.qml b/tests/auto/qml/qqmlecmascript/data/ownershipQmlIncubated.qml new file mode 100644 index 0000000000..6f536b27ca --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/ownershipQmlIncubated.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 + +Item { + id: root + + property QtObject incubatedItem + + Component.onCompleted: { + var component = Qt.createComponent("PropertyVarBaseItem.qml"); + + var incubator = component.incubateObject(root); + if (incubator.status != Component.Ready) { + incubator.onStatusChanged = function(status) { + if (status == Component.Ready) { + incubatedItem = incubator.object; + } + } + } else { + incubatedItem = incubator.object; + } + } + + function deleteIncubatedItem() { + incubatedItem.destroy(); + gc(); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/replaceBinding.qml b/tests/auto/qml/qqmlecmascript/data/replaceBinding.qml new file mode 100644 index 0000000000..670231a144 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/replaceBinding.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 + +OuterObject { + property bool success: false + + inner.foo1: 200 + inner.foo2: { return 200; } + inner.foo3: 200 + inner.foo4: { return 200; } + + inner.bar1: 'Goodbye' + inner.bar2: { return 'Goodbye' } + inner.bar3: 'Goodbye' + inner.bar4: { return 'Goodbye' } + + Component.onCompleted: { + success = (inner.foo1 == 200 && + inner.foo2 == 200 && + inner.foo3 == 200 && + inner.foo4 == 200 && + inner.bar1 == 'Goodbye' && + inner.bar2 == 'Goodbye' && + inner.bar3 == 'Goodbye' && + inner.bar4 == 'Goodbye'); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/singleV8BindingDestroyedDuringEvaluation.qml b/tests/auto/qml/qqmlecmascript/data/singleV8BindingDestroyedDuringEvaluation.qml new file mode 100644 index 0000000000..ae84f028a5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/singleV8BindingDestroyedDuringEvaluation.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 +import Qt.test 1.0 + +Item { + MyQmlObject { + value: if (1) 3 + } + + MyQmlObject { + value: { deleteMe(), 2 } + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index b3bf92fe81..c2d490e3c5 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -135,6 +135,7 @@ private slots: void ownershipCustomReturnValue(); void ownershipRootObject(); void ownershipConsistency(); + void ownershipQmlIncubated(); void qlistqobjectMethods(); void strictlyEquals(); void compiled(); @@ -183,7 +184,7 @@ private slots: void assignSequenceTypes(); void qtbug_22464(); void qtbug_21580(); - + void singleV8BindingDestroyedDuringEvaluation(); void bug1(); void bug2(); void dynamicCreationCrash(); @@ -246,12 +247,13 @@ private slots: void invokableWithQObjectDerived(); void realTypePrecision(); void registeredFlagMethod(); - + void deleteLaterObjectMethodCall(); void automaticSemicolon(); void unaryExpression(); void switchStatement(); void withStatement(); void tryStatement(); + void replaceBinding(); private: static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -1902,6 +1904,16 @@ void tst_qqmlecmascript::qtbug_21580() delete object; } +// Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation +void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation() +{ + QQmlComponent component(&engine, testFileUrl("singleV8BindingDestroyedDuringEvaluation.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; +} + // QTBUG-6781 void tst_qqmlecmascript::bug1() { @@ -3009,6 +3021,24 @@ void tst_qqmlecmascript::ownershipConsistency() delete object; } +void tst_qqmlecmascript::ownershipQmlIncubated() +{ + QQmlComponent component(&engine, testFileUrl("ownershipQmlIncubated.qml")); + QObject *object = component.create(); + QVERIFY(object); + + QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0); + + QMetaObject::invokeMethod(object, "deleteIncubatedItem"); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplication::processEvents(); + + QVERIFY(object->property("incubatedItem").value<QObject*>() == 0); + + delete object; +} + class QListQObjectMethodsObject : public QObject { Q_OBJECT @@ -6000,6 +6030,13 @@ void tst_qqmlecmascript::dynamicString() QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!")); } +void tst_qqmlecmascript::deleteLaterObjectMethodCall() +{ + QQmlComponent component(&engine, testFileUrl("deleteLaterObjectMethodCall.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + void tst_qqmlecmascript::automaticSemicolon() { QQmlComponent component(&engine, testFileUrl("automaticSemicolon.qml")); @@ -6408,6 +6445,18 @@ void tst_qqmlecmascript::registeredFlagMethod() delete object; } +// QTBUG-23138 +void tst_qqmlecmascript::replaceBinding() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("replaceBinding.qml")); + QObject *obj = c.create(); + QVERIFY(obj != 0); + + QVERIFY(obj->property("success").toBool()); + delete obj; +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 837a9d2604..4720269313 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -181,7 +181,7 @@ private slots: private: QQmlEngine engine; - void testType(const QString& qml, const QString& type, const QString& error); + void testType(const QString& qml, const QString& type, const QString& error, bool partialMatch = false); }; #define DETERMINE_ERRORS(errorfile,expected,actual)\ @@ -1480,7 +1480,7 @@ void tst_qqmllanguage::reservedWords() } // Check that first child of qml is of given type. Empty type insists on error. -void tst_qqmllanguage::testType(const QString& qml, const QString& type, const QString& expectederror) +void tst_qqmllanguage::testType(const QString& qml, const QString& type, const QString& expectederror, bool partialMatch) { QQmlComponent component(&engine); component.setData(qml.toUtf8(), TEST_FILE("empty.qml")); // just a file for relative local imports @@ -1495,7 +1495,7 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q actualerror.append("; "); actualerror.append(e.description()); } - QCOMPARE(actualerror,expectederror); + QCOMPARE(actualerror.left(partialMatch ? expectederror.length(): -1),expectederror); } else { VERIFY_ERRORS(0); QObject *object = component.create(); @@ -1616,13 +1616,13 @@ void tst_qqmllanguage::importsBuiltin_data() "import com.nokia.Test 1.12\n" "Test {}" << (!qmlCheckTypes()?"TestType2":"") - << (!qmlCheckTypes()?"":"Test is ambiguous. Found in com/nokia/Test in version 1.12 and 1.11"); + << (!qmlCheckTypes()?"":"Test is ambiguous. Found in com/nokia/Test/ in version 1.12 and 1.11"); QTest::newRow("multiversion 2") << "import com.nokia.Test 1.11\n" "import com.nokia.Test 1.12\n" "OldTest {}" << (!qmlCheckTypes()?"TestType":"") - << (!qmlCheckTypes()?"":"OldTest is ambiguous. Found in com/nokia/Test in version 1.12 and 1.11"); + << (!qmlCheckTypes()?"":"OldTest is ambiguous. Found in com/nokia/Test/ in version 1.12 and 1.11"); QTest::newRow("qualified multiversion 3") << "import com.nokia.Test 1.0 as T0\n" "import com.nokia.Test 1.8 as T8\n" @@ -1691,7 +1691,7 @@ void tst_qqmllanguage::importsLocal_data() "import com.nokia.Test 1.0\n" "Test {}" << (!qmlCheckTypes()?"TestType":"") - << (!qmlCheckTypes()?"":"Test is ambiguous. Found in com/nokia/Test and in subdir"); + << (!qmlCheckTypes()?"":"Test is ambiguous. Found in com/nokia/Test/ and in subdir/"); } void tst_qqmllanguage::importsLocal() @@ -1841,32 +1841,37 @@ void tst_qqmllanguage::importsOrder_data() QTest::addColumn<QString>("qml"); QTest::addColumn<QString>("type"); QTest::addColumn<QString>("error"); + QTest::addColumn<bool>("partialMatch"); QTest::newRow("double import") << "import com.nokia.installedtest 1.4\n" "import com.nokia.installedtest 1.4\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickText":"") - << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest in version 1.4 and 1.4"); + << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest/ in version 1.4 and 1.4") + << false; QTest::newRow("installed import overrides 1") << "import com.nokia.installedtest 1.0\n" "import com.nokia.installedtest 1.4\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickText":"") - << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest in version 1.4 and 1.0"); + << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest/ in version 1.4 and 1.0") + << false; QTest::newRow("installed import overrides 2") << "import com.nokia.installedtest 1.4\n" "import com.nokia.installedtest 1.0\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickRectangle":"") - << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest in version 1.0 and 1.4"); + << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest/ in version 1.0 and 1.4") + << false; QTest::newRow("installed import re-overrides 1") << "import com.nokia.installedtest 1.4\n" "import com.nokia.installedtest 1.0\n" "import com.nokia.installedtest 1.4\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickText":"") - << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest in version 1.4 and 1.0"); + << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest/ in version 1.4 and 1.0") + << false; QTest::newRow("installed import re-overrides 2") << "import com.nokia.installedtest 1.4\n" "import com.nokia.installedtest 1.0\n" @@ -1874,41 +1879,47 @@ void tst_qqmllanguage::importsOrder_data() "import com.nokia.installedtest 1.0\n" "InstalledTest {}" << (!qmlCheckTypes()?"QQuickRectangle":"") - << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest in version 1.0 and 1.4"); - + << (!qmlCheckTypes()?"":"InstalledTest is ambiguous. Found in lib/com/nokia/installedtest/ in version 1.0 and 1.4") + << false; QTest::newRow("installed import versus builtin 1") << "import com.nokia.installedtest 1.5\n" "import QtQuick 2.0\n" "Rectangle {}" << (!qmlCheckTypes()?"QQuickRectangle":"") - << (!qmlCheckTypes()?"":"Rectangle is ambiguous. Found in Qt and in lib/com/nokia/installedtest"); + << (!qmlCheckTypes()?"":"Rectangle is ambiguous. Found in file://") + << true; QTest::newRow("installed import versus builtin 2") << "import QtQuick 2.0\n" "import com.nokia.installedtest 1.5\n" "Rectangle {}" << (!qmlCheckTypes()?"QQuickText":"") - << (!qmlCheckTypes()?"":"Rectangle is ambiguous. Found in lib/com/nokia/installedtest and in Qt"); + << (!qmlCheckTypes()?"":"Rectangle is ambiguous. Found in lib/com/nokia/installedtest/ and in file://") + << true; QTest::newRow("namespaces cannot be overridden by types 1") << "import QtQuick 2.0 as Rectangle\n" "import com.nokia.installedtest 1.5\n" "Rectangle {}" - << "" - << "Namespace Rectangle cannot be used as a type"; + << "" + << "Namespace Rectangle cannot be used as a type" + << false; QTest::newRow("namespaces cannot be overridden by types 2") << "import QtQuick 2.0 as Rectangle\n" "import com.nokia.installedtest 1.5\n" "Rectangle.Image {}" - << "QQuickImage" - << ""; + << "QQuickImage" + << "" + << false; QTest::newRow("local last 1") << "LocalLast {}" - << "QQuickText" - << ""; + << "QQuickText" + << "" + << false; QTest::newRow("local last 2") << "import com.nokia.installedtest 1.0\n" "LocalLast {}" << (!qmlCheckTypes()?"QQuickRectangle":"")// i.e. from com.nokia.installedtest, not data/LocalLast.qml - << (!qmlCheckTypes()?"":"LocalLast is ambiguous. Found in lib/com/nokia/installedtest and in local directory"); + << (!qmlCheckTypes()?"":"LocalLast is ambiguous. Found in lib/com/nokia/installedtest/ and in ") + << false; } void tst_qqmllanguage::importsOrder() @@ -1916,7 +1927,8 @@ void tst_qqmllanguage::importsOrder() QFETCH(QString, qml); QFETCH(QString, type); QFETCH(QString, error); - testType(qml,type,error); + QFETCH(bool, partialMatch); + testType(qml,type,error,partialMatch); } void tst_qqmllanguage::importIncorrectCase() diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt new file mode 100644 index 0000000000..262193788b --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.errors.txt @@ -0,0 +1 @@ +1:1:module "com.nokia.AutoTestQmlNestedPluginType.Nested" is not installed diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml new file mode 100644 index 0000000000..b3f9ac6c3f --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.1.qml @@ -0,0 +1,5 @@ +import com.nokia.AutoTestQmlNestedPluginType.Nested 1.0 +import com.nokia.AutoTestQmlNestedPluginType 1.0 + +MyNestedPluginType { +} diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.2.qml b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.2.qml new file mode 100644 index 0000000000..cb8e0e33d1 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.2.qml @@ -0,0 +1,5 @@ +import com.nokia.AutoTestQmlNestedPluginType 1.0 +import com.nokia.AutoTestQmlNestedPluginType.Nested 1.0 + +MyNestedPluginType { +} diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.3.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.3.errors.txt new file mode 100644 index 0000000000..f0c73e336f --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.3.errors.txt @@ -0,0 +1 @@ +3:1:MyNestedPluginType is not a type diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.3.qml b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.3.qml new file mode 100644 index 0000000000..69c6a34f46 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.3.qml @@ -0,0 +1,4 @@ +import com.nokia.AutoTestQmlNestedPluginType 1.0 + +MyNestedPluginType { +} diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.4.errors.txt b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.4.errors.txt new file mode 100644 index 0000000000..9743ae4f68 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.4.errors.txt @@ -0,0 +1 @@ +2:1:module "com.nokia.AutoTestQmlNestedPluginType.Nested" version 6.66 is not installed diff --git a/tests/auto/qml/qqmlmoduleplugin/data/importsNested.4.qml b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.4.qml new file mode 100644 index 0000000000..dce8b7564a --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/data/importsNested.4.qml @@ -0,0 +1,5 @@ +import com.nokia.AutoTestQmlNestedPluginType 1.0 +import com.nokia.AutoTestQmlNestedPluginType.Nested 6.66 + +MyNestedPluginType { +} diff --git a/tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlNestedPluginType/qmldir b/tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlNestedPluginType/qmldir new file mode 100644 index 0000000000..f6ed20dda4 --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlNestedPluginType/qmldir @@ -0,0 +1 @@ +plugin nestedPlugin diff --git a/src/plugins/qmltooling/qmldbg_qtquick2/selectiontool.cpp b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp index 5ad102a92d..2d0af471c2 100644 --- a/src/plugins/qmltooling/qmldbg_qtquick2/selectiontool.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage @@ -38,55 +38,52 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +#include <QStringList> +#include <QtQml/qqmlextensionplugin.h> +#include <QtQml/qqml.h> +#include <QDebug> -#include "selectiontool.h" - -#include "highlight.h" -#include "qquickviewinspector.h" +class MyPluginType : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString value READ value) -#include <QtGui/QMouseEvent> -#include <QtQuick/QQuickView> -#include <QtQuick/QQuickItem> +public: + MyPluginType(QObject *parent=0) : QObject(parent) {} -namespace QmlJSDebugger { -namespace QtQuick2 { + QString value() const { return "Hello"; } +}; -SelectionTool::SelectionTool(QQuickViewInspector *inspector) : - AbstractTool(inspector), - m_hoverHighlight(new HoverHighlight(inspector->overlay())) +class MyNestedPluginType : public QObject { -} + Q_OBJECT + Q_PROPERTY(QString value READ value) -void SelectionTool::leaveEvent(QEvent *) -{ - m_hoverHighlight->setVisible(false); -} +public: + MyNestedPluginType(QObject *parent=0) : QObject(parent) {} -void SelectionTool::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) { - if (QQuickItem *item = inspector()->topVisibleItemAt(event->pos())) - inspector()->setSelectedItems(QList<QQuickItem*>() << item); - } else if (event->button() == Qt::RightButton) { - // todo: Show context menu - } -} + QString value() const { return "Goodbye"; } +}; -void SelectionTool::hoverMoveEvent(QMouseEvent *event) -{ - QQuickItem *item = inspector()->topVisibleItemAt(event->pos()); - if (!item) { - m_hoverHighlight->setVisible(false); - } else { - m_hoverHighlight->setItem(item); - m_hoverHighlight->setVisible(true); - } -} -QQuickViewInspector *SelectionTool::inspector() const +class MyPlugin : public QQmlExtensionPlugin { - return static_cast<QQuickViewInspector*>(AbstractTool::inspector()); -} + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface" FILE "../empty.json") + +public: + MyPlugin() {} + + void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == "com.nokia.AutoTestQmlNestedPluginType"); + qmlRegisterType<MyPluginType>(uri, 1, 0, "MyPluginType"); + + QString nestedUri(uri); + nestedUri += QLatin1String(".Nested"); + + qmlRegisterType<MyNestedPluginType>(nestedUri.toLatin1().constData(), 1, 0, "MyNestedPluginType"); + } +}; -} // namespace QtQuick2 -} // namespace QmlJSDebugger +#include "nestedPlugin.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.pro b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.pro new file mode 100644 index 0000000000..94dc236a4c --- /dev/null +++ b/tests/auto/qml/qqmlmoduleplugin/nestedPlugin/nestedPlugin.pro @@ -0,0 +1,7 @@ +TEMPLATE = lib +CONFIG += nestedPlugin +SOURCES = nestedPlugin.cpp +QT = core qml +DESTDIR = ../imports/com/nokia/AutoTestQmlNestedPluginType + +QT += core-private gui-private qml-private diff --git a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro index 42eedc20f2..6da88320cd 100644 --- a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro @@ -1,6 +1,6 @@ QT = core TEMPLATE = subdirs -SUBDIRS = plugin plugin.2 plugin.2.1 pluginWrongCase pluginWithQmlFile pluginMixed pluginVersion +SUBDIRS = plugin plugin.2 plugin.2.1 pluginWrongCase pluginWithQmlFile pluginMixed pluginVersion nestedPlugin tst_qqmlmoduleplugin_pro.depends += plugin SUBDIRS += tst_qqmlmoduleplugin.pro diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index b574bad595..f6c165840d 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -70,6 +70,8 @@ private slots: void versionNotInstalled_data(); void implicitQmldir(); void implicitQmldir_data(); + void importsNested(); + void importsNested_data(); private: QString m_importsDirectory; @@ -347,6 +349,47 @@ void tst_qqmlmoduleplugin::implicitQmldir() delete obj; } +void tst_qqmlmoduleplugin::importsNested_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QString>("errorFile"); + + // Note: no other test case should import the plugin used for this test, or the + // wrong order test will pass spuriously + QTest::newRow("wrongOrder") << "importsNested.1.qml" << "importsNested.1.errors.txt"; + QTest::newRow("missingImport") << "importsNested.3.qml" << "importsNested.3.errors.txt"; + QTest::newRow("invalidVersion") << "importsNested.4.qml" << "importsNested.4.errors.txt"; + QTest::newRow("correctOrder") << "importsNested.2.qml" << QString(); +} +void tst_qqmlmoduleplugin::importsNested() +{ + QFETCH(QString, file); + QFETCH(QString, errorFile); + + // Note: because imports are cached between test case data rows (and the plugins remain loaded), + // these tests should really be run in new instances of the app... + + QQmlEngine engine; + engine.addImportPath(m_importsDirectory); + + if (!errorFile.isEmpty()) { + QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); + } + + QQmlComponent component(&engine, testFile(file)); + QObject *obj = component.create(); + + if (errorFile.isEmpty()) { + if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) + qWarning() << "Unexpected Errors:" << component.errors(); + QVERIFY(obj); + delete obj; + } else { + QList<QQmlError> errors = component.errors(); + VERIFY_ERRORS(errorFile.toLatin1().constData()); + QVERIFY(!obj); + } +} QTEST_MAIN(tst_qqmlmoduleplugin) diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index 00b64e04a1..cb42be91d7 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -165,6 +165,10 @@ and ensures that the subnode's source locations are inside parent node's source void tst_qqmlparser::qmlParser_data() { +#if defined(QTEST_CROSS_COMPILED) + return; +#endif + QTest::addColumn<QString>("file"); QString examples = QLatin1String(SRCDIR) + "/../../../../examples/"; @@ -180,11 +184,10 @@ void tst_qqmlparser::qmlParser_data() void tst_qqmlparser::qmlParser() { - QFETCH(QString, file); - #if defined(QTEST_CROSS_COMPILED) QSKIP("sources not available when cross compiled"); #endif + QFETCH(QString, file); using namespace QQmlJS; diff --git a/tests/auto/qml/qqmlproperty/data/invalidBinding.qml b/tests/auto/qml/qqmlproperty/data/invalidBinding.qml new file mode 100644 index 0000000000..e2bb1d172d --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/invalidBinding.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Item { + property Text text: myText + + property Rectangle rectangle1: myText + property Rectangle rectangle2: eval('getMyText()') // eval to force non-shared (v8) binding + + function getMyText() { return myText; } + + Text { + id: myText + + anchors.verticalCenter: parent // invalid binding + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index aa22450bd3..f2521e93ef 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -45,6 +45,7 @@ #include <QtQml/qqmlproperty.h> #include <QtQml/private/qqmlproperty_p.h> #include <private/qqmlbinding_p.h> +#include <private/qqmlboundsignal_p.h> #include <QtWidgets/QLineEdit> #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> @@ -133,6 +134,7 @@ private slots: void aliasPropertyBindings(); void noContext(); void assignEmptyVariantMap(); + void warnOnInvalidBinding(); void copy(); private: @@ -145,8 +147,9 @@ void tst_qqmlproperty::qmlmetaproperty() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -178,8 +181,8 @@ void tst_qqmlproperty::qmlmetaproperty() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -248,8 +251,9 @@ void tst_qqmlproperty::qmlmetaproperty_object() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -281,8 +285,8 @@ void tst_qqmlproperty::qmlmetaproperty_object() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -295,8 +299,9 @@ void tst_qqmlproperty::qmlmetaproperty_object() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -330,8 +335,8 @@ void tst_qqmlproperty::qmlmetaproperty_object() QVERIFY(binding != 0); QVERIFY(QQmlPropertyPrivate::binding(prop) == binding.data()); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -349,8 +354,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -382,8 +388,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -396,8 +402,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -431,8 +438,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(binding != 0); QVERIFY(QQmlPropertyPrivate::binding(prop) == binding.data()); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -445,8 +452,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -478,9 +486,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression != 0); - QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == expression.data()); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(!sigExprWatcher.wasDeleted()); + QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -493,8 +501,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -526,9 +535,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression != 0); - QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == expression.data()); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(!sigExprWatcher.wasDeleted()); + QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -546,8 +555,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -579,8 +589,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -593,8 +603,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -628,8 +639,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_context() QVERIFY(binding != 0); QVERIFY(QQmlPropertyPrivate::binding(prop) == binding.data()); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -647,8 +658,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -680,8 +692,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), -1); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -694,8 +706,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -729,8 +742,8 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(binding != 0); QVERIFY(QQmlPropertyPrivate::binding(prop) == binding.data()); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression == 0); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(sigExprWatcher.wasDeleted()); QCOMPARE(prop.index(), dobject.metaObject()->indexOfProperty("defaultProperty")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -743,8 +756,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -776,9 +790,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression != 0); - QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == expression.data()); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(!sigExprWatcher.wasDeleted()); + QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("clicked()")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -791,8 +805,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QWeakPointer<QQmlAbstractBinding> binding(QQmlAbstractBinding::getPointer(new QQmlBinding(QLatin1String("null"), 0, engine.rootContext()))); static_cast<QQmlBinding *>(binding.data())->setTarget(prop); QVERIFY(binding != 0); - QWeakPointer<QQmlExpression> expression(new QQmlExpression()); - QVERIFY(expression != 0); + QQmlBoundSignalExpression *sigExpr = new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1); + QQmlAbstractExpression::DeleteWatcher sigExprWatcher(sigExpr); + QVERIFY(sigExpr != 0 && !sigExprWatcher.wasDeleted()); QObject *obj = new QObject; @@ -824,9 +839,9 @@ void tst_qqmlproperty::qmlmetaproperty_object_string_context() QVERIFY(QQmlPropertyPrivate::setBinding(prop, binding.data()) == 0); QVERIFY(binding == 0); QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == 0); - QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, expression.data()) == 0); - QVERIFY(expression != 0); - QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == expression.data()); + QVERIFY(QQmlPropertyPrivate::setSignalExpression(prop, sigExpr) == 0); + QVERIFY(!sigExprWatcher.wasDeleted()); + QVERIFY(QQmlPropertyPrivate::signalExpression(prop) == sigExpr); QCOMPARE(prop.index(), dobject.metaObject()->indexOfMethod("oddlyNamedNotifySignal()")); QCOMPARE(QQmlPropertyPrivate::valueTypeCoreIndex(prop), -1); @@ -972,7 +987,7 @@ void tst_qqmlproperty::read() QQmlProperty p(&o, "onClicked"); QCOMPARE(p.read(), QVariant()); - QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlExpression())); + QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1))); QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.read(), QVariant()); @@ -984,7 +999,7 @@ void tst_qqmlproperty::read() QQmlProperty p(&o, "onPropertyWithNotifyChanged"); QCOMPARE(p.read(), QVariant()); - QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlExpression())); + QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1))); QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.read(), QVariant()); @@ -1140,7 +1155,7 @@ void tst_qqmlproperty::write() QQmlProperty p(&o, "onClicked"); QCOMPARE(p.write(QVariant("console.log(1921)")), false); - QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlExpression())); + QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1))); QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.write(QVariant("console.log(1921)")), false); @@ -1154,7 +1169,7 @@ void tst_qqmlproperty::write() QQmlProperty p(&o, "onPropertyWithNotifyChanged"); QCOMPARE(p.write(QVariant("console.log(1921)")), false); - QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlExpression())); + QVERIFY(0 == QQmlPropertyPrivate::setSignalExpression(p, new QQmlBoundSignalExpression(QQmlContextData::get(engine.rootContext()), 0, QLatin1String("null"), false, QString(), -1, -1))); QVERIFY(0 != QQmlPropertyPrivate::signalExpression(p)); QCOMPARE(p.write(QVariant("console.log(1921)")), false); @@ -1708,6 +1723,29 @@ void tst_qqmlproperty::assignEmptyVariantMap() delete obj; } +void tst_qqmlproperty::warnOnInvalidBinding() +{ + QUrl testUrl(testFileUrl("invalidBinding.qml")); + QString expectedWarning; + + // V4 error message for property-to-property binding + expectedWarning = testUrl.toString() + QString::fromLatin1(":6:36: Unable to assign QQuickText to QQuickRectangle"); + QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + + // V8 error message for function-to-property binding + expectedWarning = testUrl.toString() + QString::fromLatin1(":7:36: Unable to assign QQuickText to QQuickRectangle"); + QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + + // V8 error message for invalid binding to anchor + expectedWarning = testUrl.toString() + QString::fromLatin1(":14: Unable to assign QQuickItem_QML_7 to QQuickAnchorLine"); + QTest::ignoreMessage(QtWarningMsg, expectedWarning.toLatin1().constData()); + + QQmlComponent component(&engine, testUrl); + QObject *obj = component.create(); + QVERIFY(obj); + delete obj; +} + void tst_qqmlproperty::initTestCase() { QQmlDataTest::initTestCase(); diff --git a/tests/auto/qml/qqmlqt/data/dateTimeConversion.qml b/tests/auto/qml/qqmlqt/data/dateTimeConversion.qml index 641ba6e1ca..300074dec1 100644 --- a/tests/auto/qml/qqmlqt/data/dateTimeConversion.qml +++ b/tests/auto/qml/qqmlqt/data/dateTimeConversion.qml @@ -11,4 +11,10 @@ QtObject { property variant qdatetime4: new Date(2001,1,2) // 2001/02/02 hh:mm:ss.zzz property variant qdatetime5: new Date(1999,0,1,2,3,4) // 1999/01/01 02:03:04.zzz property variant qdatetime6: new Date(2008,1,24,14,15,38,200) // 2008/02/24 14:15:38.200 + + // Use UTC for historical dates to avoid DST issues + property variant qdatetime7: new Date(Date.UTC(1970,0,1,0,0,0,0)) // 1970/01/01 00:00:00.000 + property variant qdatetime8: new Date(Date.UTC(1586,1,2)) // 1586/02/02 hh:mm:ss.zzz + property variant qdatetime9: new Date(Date.UTC(955,0,1,0,0,0,0)) // 955/01/01 00:00:00.000 + property variant qdatetime10: new Date(Date.UTC(113,1,24,14,15,38,200)) // 113/02/24 14:15:38.200 } diff --git a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp index a1d7291565..c81e6771b8 100644 --- a/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp +++ b/tests/auto/qml/qqmlqt/tst_qqmlqt.cpp @@ -480,6 +480,10 @@ void tst_qqmlqt::dateTimeConversion() QDateTime dateTime4(QDate(2001,2,2), QTime(0,0,0,0)); QDateTime dateTime5(QDate(1999,1,1), QTime(2,3,4,0)); QDateTime dateTime6(QDate(2008,2,24), QTime(14,15,38,200)); + QDateTime dateTime7(QDate(1970,1,1), QTime(0,0,0,0), Qt::UTC); + QDateTime dateTime8(QDate(1586,2,2), QTime(0,0,0,0), Qt::UTC); + QDateTime dateTime9(QDate(955,1,1), QTime(0,0,0,0), Qt::UTC); + QDateTime dateTime10(QDate(113,2,24), QTime(14,15,38,200), Qt::UTC); QQmlEngine eng; QQmlComponent component(&eng, testFileUrl("dateTimeConversion.qml")); @@ -493,6 +497,10 @@ void tst_qqmlqt::dateTimeConversion() QCOMPARE(obj->property("qdatetime4").toDateTime(), dateTime4); QCOMPARE(obj->property("qdatetime5").toDateTime(), dateTime5); QCOMPARE(obj->property("qdatetime6").toDateTime(), dateTime6); + QCOMPARE(obj->property("qdatetime7").toDateTime(), dateTime7); + QCOMPARE(obj->property("qdatetime8").toDateTime(), dateTime8); + QCOMPARE(obj->property("qdatetime9").toDateTime(), dateTime9); + QCOMPARE(obj->property("qdatetime10").toDateTime(), dateTime10); } void tst_qqmlqt::dateTimeFormatting() diff --git a/tests/auto/qml/v4/data/conversions.1.qml b/tests/auto/qml/v4/data/conversions.1.qml new file mode 100644 index 0000000000..b3abde770a --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.1.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning bool prop to other proptypes. + boolProp: true + intProp: boolProp + floatProp: boolProp + doubleProp: boolProp + qrealProp: boolProp + qstringProp: boolProp + qurlProp: boolProp + vec3Prop: Qt.vector3d(boolProp, boolProp, boolProp) +} diff --git a/tests/auto/qml/v4/data/conversions.2.qml b/tests/auto/qml/v4/data/conversions.2.qml new file mode 100644 index 0000000000..2fd0453ac2 --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.2.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning int prop to other proptypes. + boolProp: intProp + intProp: 4 + floatProp: intProp + doubleProp: intProp + qrealProp: intProp + qstringProp: intProp + qurlProp: intProp + vec3Prop: Qt.vector3d(intProp, intProp, intProp) +} diff --git a/tests/auto/qml/v4/data/conversions.3.qml b/tests/auto/qml/v4/data/conversions.3.qml new file mode 100644 index 0000000000..66f0761a25 --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.3.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning float prop to other proptypes. + boolProp: floatProp + intProp: floatProp + floatProp: 4.4 + doubleProp: floatProp + qrealProp: floatProp + qstringProp: floatProp + qurlProp: floatProp + vec3Prop: Qt.vector3d(floatProp, floatProp, floatProp) +} diff --git a/tests/auto/qml/v4/data/conversions.4.qml b/tests/auto/qml/v4/data/conversions.4.qml new file mode 100644 index 0000000000..ccf0035313 --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.4.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning double prop to other prop types + boolProp: doubleProp + intProp: doubleProp + floatProp: doubleProp + doubleProp: 4.444444444 + qrealProp: doubleProp + qstringProp: doubleProp + qurlProp: doubleProp + vec3Prop: Qt.vector3d(doubleProp, doubleProp, doubleProp) +} diff --git a/tests/auto/qml/v4/data/conversions.5.qml b/tests/auto/qml/v4/data/conversions.5.qml new file mode 100644 index 0000000000..26dc3b7195 --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.5.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning qreal prop to other prop types + boolProp: qrealProp + intProp: qrealProp + floatProp: qrealProp + doubleProp: qrealProp + qrealProp: 4.44 + qstringProp: qrealProp + qurlProp: qrealProp + vec3Prop: Qt.vector3d(qrealProp, qrealProp, qrealProp) +} diff --git a/tests/auto/qml/v4/data/conversions.6.qml b/tests/auto/qml/v4/data/conversions.6.qml new file mode 100644 index 0000000000..573b227ada --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.6.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning string prop to other proptypes. + boolProp: qstringProp + intProp: qstringProp + floatProp: qstringProp + doubleProp: qstringProp + qrealProp: qstringProp + qstringProp: "4" + qurlProp: qstringProp + vec3Prop: Qt.vector3d(qstringProp, qstringProp, qstringProp) +} diff --git a/tests/auto/qml/v4/data/conversions.7.qml b/tests/auto/qml/v4/data/conversions.7.qml new file mode 100644 index 0000000000..5112b06b1e --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.7.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning url prop to other proptypes. + boolProp: qurlProp + intProp: qurlProp + floatProp: qurlProp + doubleProp: qurlProp + qrealProp: qurlProp + qstringProp: qurlProp + qurlProp: "4" + vec3Prop: Qt.vector3d(qurlProp, qurlProp, qurlProp) +} diff --git a/tests/auto/qml/v4/data/conversions.8.qml b/tests/auto/qml/v4/data/conversions.8.qml new file mode 100644 index 0000000000..18bf160e7e --- /dev/null +++ b/tests/auto/qml/v4/data/conversions.8.qml @@ -0,0 +1,13 @@ +import Qt.v4 1.0 + +Conversion { + // test assigning vector prop to other proptypes. + boolProp: vec3Prop + intProp: vec3Prop + floatProp: vec3Prop + doubleProp: vec3Prop + qrealProp: vec3Prop + qstringProp: vec3Prop + qurlProp: vec3Prop + vec3Prop: Qt.vector3d(4, 4, 4) +} diff --git a/tests/auto/qml/v4/data/subscriptions.1.qml b/tests/auto/qml/v4/data/subscriptions.1.qml new file mode 100644 index 0000000000..607233819e --- /dev/null +++ b/tests/auto/qml/v4/data/subscriptions.1.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 + +Rectangle { + id: root + width: 400 + height: 400 + + property real targetHeight: menuItems.height + 1 + property real heightValue: if (1) menuItems.height //this must be v8? + property bool boolProp: menuItems.height > heightValue //this must be v4? + + Column { + id: menuItems + Item { height: 200; width: 10 } + } +} diff --git a/tests/auto/qml/v4/testtypes.cpp b/tests/auto/qml/v4/testtypes.cpp index c879cf2226..80ccf007a5 100644 --- a/tests/auto/qml/v4/testtypes.cpp +++ b/tests/auto/qml/v4/testtypes.cpp @@ -46,4 +46,5 @@ void registerTypes() { qmlRegisterType<ResultObject>("Qt.v4", 1,0, "Result"); qmlRegisterType<NestedObject>(); + qmlRegisterType<ConversionObject>("Qt.v4", 1, 0, "Conversion"); } diff --git a/tests/auto/qml/v4/testtypes.h b/tests/auto/qml/v4/testtypes.h index 18b5b27b36..04dd82e8fe 100644 --- a/tests/auto/qml/v4/testtypes.h +++ b/tests/auto/qml/v4/testtypes.h @@ -42,6 +42,8 @@ #define TESTTYPES_H #include <QtCore/qobject.h> +#include <QtCore/qurl.h> +#include <QVector3D> class NestedObject : public QObject { @@ -80,6 +82,61 @@ private: NestedObject m_nested2; }; +class ConversionObject : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool boolProp READ boolProp WRITE setBoolProp NOTIFY boolPropChanged) + Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged) + Q_PROPERTY(float floatProp READ floatProp WRITE setFloatProp NOTIFY floatPropChanged) + Q_PROPERTY(double doubleProp READ doubleProp WRITE setDoubleProp NOTIFY doublePropChanged) + Q_PROPERTY(qreal qrealProp READ qrealProp WRITE setQrealProp NOTIFY qrealPropChanged) + Q_PROPERTY(QString qstringProp READ qstringProp WRITE setQstringProp NOTIFY qstringPropChanged) + Q_PROPERTY(QUrl qurlProp READ qurlProp WRITE setQurlProp NOTIFY qurlPropChanged) + Q_PROPERTY(QVector3D vec3Prop READ vec3Prop WRITE setVec3Prop NOTIFY vec3PropChanged) + +public: + ConversionObject() : m_boolProp(false), m_intProp(0), m_floatProp(0.0), m_doubleProp(0.0), m_qrealProp(0.0) {} + ~ConversionObject() {} + + bool boolProp() const { return m_boolProp; } + void setBoolProp(bool v) { m_boolProp = v; emit boolPropChanged(); } + int intProp() const { return m_intProp; } + void setIntProp(int v) { m_intProp = v; emit intPropChanged(); } + float floatProp() const { return m_floatProp; } + void setFloatProp(float v) { m_floatProp = v; emit floatPropChanged(); } + double doubleProp() const { return m_doubleProp; } + void setDoubleProp(double v) { m_doubleProp = v; emit doublePropChanged(); } + qreal qrealProp() const { return m_qrealProp; } + void setQrealProp(qreal v) { m_qrealProp = v; emit qrealPropChanged(); } + QString qstringProp() const { return m_qstringProp; } + void setQstringProp(const QString& v) { m_qstringProp = v; emit qstringPropChanged(); } + QUrl qurlProp() const { return m_qurlProp; } + void setQurlProp(const QUrl& v) { m_qurlProp = v; emit qurlPropChanged(); } + QVector3D vec3Prop() const { return m_vec3Prop; } + void setVec3Prop(const QVector3D& v) { m_vec3Prop = v; emit vec3PropChanged(); } + +signals: + void boolPropChanged(); + void intPropChanged(); + void floatPropChanged(); + void doublePropChanged(); + void qrealPropChanged(); + void qstringPropChanged(); + void qurlPropChanged(); + void vec3PropChanged(); + +private: + bool m_boolProp; + int m_intProp; + float m_floatProp; + double m_doubleProp; + qreal m_qrealProp; + QString m_qstringProp; + QUrl m_qurlProp; + QVector3D m_vec3Prop; +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/v4/tst_v4.cpp b/tests/auto/qml/v4/tst_v4.cpp index 47fa10b595..dfb7cc48c7 100644 --- a/tests/auto/qml/v4/tst_v4.cpp +++ b/tests/auto/qml/v4/tst_v4.cpp @@ -46,6 +46,7 @@ #include <QtQml/qqmlcomponent.h> #include <QtCore/qdebug.h> #include <QtGui/qcolor.h> +#include <QtCore/qnumeric.h> #include <private/qv4compiler_p.h> @@ -83,6 +84,12 @@ private slots: void mathMin(); void moduleApi(); + void conversions_data(); + void conversions(); + void subscriptions(); + + void debuggingDumpInstructions(); // this test should be last. + private: QQmlEngine engine; }; @@ -135,6 +142,14 @@ void tst_v4::qtscript_data() QTest::newRow("unary minus") << "unaryMinus.qml"; QTest::newRow("null qobject") << "nullQObject.qml"; QTest::newRow("qobject -> bool") << "objectToBool.qml"; + QTest::newRow("conversion from bool") << "conversions.1.qml"; // QTBUG-24706 + QTest::newRow("conversion from int") << "conversions.2.qml"; // QTBUG-24706 + QTest::newRow("conversion from float") << "conversions.3.qml"; + QTest::newRow("conversion from double") << "conversions.4.qml"; // QTBUG-24706 + QTest::newRow("conversion from real") << "conversions.5.qml"; // QTBUG-24706 + QTest::newRow("conversion from string") << "conversions.6.qml"; // QTBUG-24706 + QTest::newRow("conversion from url") << "conversions.7.qml"; // QTBUG-24706 + QTest::newRow("conversion from vec3") << "conversions.8.qml"; } void tst_v4::unnecessaryReeval() @@ -630,6 +645,334 @@ void tst_v4::moduleApi() delete o; } +void tst_v4::conversions_data() +{ + QTest::addColumn<QUrl>("file"); + QTest::addColumn<QStringList>("warnings"); + QTest::addColumn<bool>("boolProp"); + QTest::addColumn<int>("intProp"); + QTest::addColumn<float>("floatProp"); + QTest::addColumn<double>("doubleProp"); + QTest::addColumn<qreal>("qrealProp"); + QTest::addColumn<QString>("qstringProp"); + QTest::addColumn<QUrl>("qurlProp"); + QTest::addColumn<QVector3D>("vec3Prop"); + + QTest::newRow("from bool") << testFileUrl("conversions.1.qml") + << (QStringList() << (testFileUrl("conversions.1.qml").toString() + QLatin1String(":11:15: Unable to assign bool to QUrl"))) + << true + << (int)true + << (float)1.0 + << (double)1.0 + << (qreal)1.0 + << QString(QLatin1String("true")) + << QUrl() // cannot assign bool to url. + << QVector3D(1, 1, 1); + + QTest::newRow("from integer") << testFileUrl("conversions.2.qml") + << (QStringList() << (testFileUrl("conversions.2.qml").toString() + QLatin1String(":11:15: Unable to assign int to QUrl"))) + << (bool)4 + << 4 + << (float)4.0 + << (double)4.0 + << (qreal)4.0 + << QString(QLatin1String("4")) + << QUrl() // cannot assign int to url. + << QVector3D(4, 4, 4); + + QTest::newRow("from float") << testFileUrl("conversions.3.qml") + << (QStringList() << (testFileUrl("conversions.3.qml").toString() + QLatin1String(":11:15: Unable to assign number to QUrl"))) + << (bool)4.4 + << (int)4.4 + << (float)4.4 + << (double)((float)4.4) + << (qreal)((float)4.4) + << QString::number((double)((float)4.4), 'g', 16) + << QUrl() // cannot assign number to url. + << QVector3D(4.4, 4.4, 4.4); + + QTest::newRow("from double") << testFileUrl("conversions.4.qml") + << (QStringList() << (testFileUrl("conversions.4.qml").toString() + QLatin1String(":11:15: Unable to assign number to QUrl"))) + << (bool)4.444444444 + << (int)4.444444444 + << (float)4.444444444 + << (double)4.444444444 + << (qreal)4.444444444 + << QString::number((double)4.444444444, 'g', 16) + << QUrl() // cannot assign number to url. + << QVector3D(4.444444444, 4.444444444, 4.444444444); + + QTest::newRow("from qreal") << testFileUrl("conversions.5.qml") + << (QStringList() << (testFileUrl("conversions.5.qml").toString() + QLatin1String(":11:15: Unable to assign number to QUrl"))) + << (bool)4.44 + << (int)4.44 + << (float)4.44 + << (double)4.44 + << (qreal)4.44 + << QString(QLatin1String("4.44")) + << QUrl() // cannot assign number to url. + << QVector3D(4.44, 4.44, 4.44); + + QTest::newRow("from string") << testFileUrl("conversions.6.qml") + << (QStringList()) + << true + << 4 + << (float)4.0 + << (double)4.0 + << (qreal)4.0 + << QString(QLatin1String("4")) + << QUrl(testFileUrl("").toString() + QString(QLatin1String("4"))) + << QVector3D(4, 4, 4); + + // QTBUG-24706 + QTest::newRow("from url") << testFileUrl("conversions.7.qml") + << (QStringList() << (testFileUrl("conversions.7.qml").toString() + QLatin1String(":6:14: Unable to assign QUrl to int")) + << (testFileUrl("conversions.7.qml").toString() + QLatin1String(":7:16: Unable to assign QUrl to number")) + << (testFileUrl("conversions.7.qml").toString() + QLatin1String(":8:17: Unable to assign QUrl to number")) + << (testFileUrl("conversions.7.qml").toString() + QLatin1String(":9:16: Unable to assign QUrl to number"))) + << true + << 0 + << (float) 0 + << (double) 0 + << (qreal) 0 + << QString(testFileUrl("").toString() + QString(QLatin1String("4"))) + << QUrl(testFileUrl("").toString() + QString(QLatin1String("4"))) + << QVector3D(qQNaN(), qQNaN(), qQNaN()); + + QTest::newRow("from vector") << testFileUrl("conversions.8.qml") + << (QStringList() << (testFileUrl("conversions.8.qml").toString() + QLatin1String(":11: Unable to assign QVector3D to QUrl")) + << (testFileUrl("conversions.8.qml").toString() + QLatin1String(":10: Unable to assign QVector3D to QString")) + << (testFileUrl("conversions.8.qml").toString() + QLatin1String(":9: Unable to assign QVector3D to double")) + << (testFileUrl("conversions.8.qml").toString() + QLatin1String(":8: Unable to assign QVector3D to double")) + << (testFileUrl("conversions.8.qml").toString() + QLatin1String(":7: Unable to assign QVector3D to float")) + << (testFileUrl("conversions.8.qml").toString() + QLatin1String(":6: Unable to assign QVector3D to int"))) + << true // non-null therefore true + << (int)0 // the other values should be the default-ctor values. + << (float)0 + << (double)0 + << (qreal)0 + << QString() + << QUrl() + << QVector3D(4, 4, 4); // except this one. +} + +#define COMPARE_NUMBER(type, prop, expected) \ + if (qIsNaN(expected)) \ + QVERIFY(qIsNaN(qvariant_cast<type>(prop))); \ + else \ + QCOMPARE((prop), QVariant::fromValue<type>(expected)); + +void tst_v4::conversions() +{ + QFETCH(QUrl, file); + QFETCH(QStringList, warnings); + QFETCH(bool, boolProp); + QFETCH(int, intProp); + QFETCH(float, floatProp); + QFETCH(double, doubleProp); + QFETCH(qreal, qrealProp); + QFETCH(QString, qstringProp); + QFETCH(QUrl, qurlProp); + QFETCH(QVector3D, vec3Prop); + + foreach (const QString &w, warnings) + QTest::ignoreMessage(QtWarningMsg, qPrintable(w)); + + QQmlComponent component(&engine, file); + QObject *o = component.create(); + QVERIFY(o != 0); + QCOMPARE(o->property("boolProp"), QVariant::fromValue<bool>(boolProp)); + QCOMPARE(o->property("intProp"), QVariant::fromValue<int>(intProp)); + COMPARE_NUMBER(float, o->property("floatProp"), floatProp); + COMPARE_NUMBER(double, o->property("doubleProp"), doubleProp); + COMPARE_NUMBER(qreal, o->property("qrealProp"), qrealProp); + QCOMPARE(o->property("qstringProp"), QVariant::fromValue<QString>(qstringProp)); + QCOMPARE(o->property("qurlProp"), QVariant::fromValue<QUrl>(qurlProp)); + + QVector3D vec3 = qvariant_cast<QVector3D>(o->property("vec3Prop")); + COMPARE_NUMBER(qreal, QVariant::fromValue<qreal>(vec3.x()), vec3Prop.x()); + COMPARE_NUMBER(qreal, QVariant::fromValue<qreal>(vec3.y()), vec3Prop.y()); + COMPARE_NUMBER(qreal, QVariant::fromValue<qreal>(vec3.z()), vec3Prop.z()); + delete o; +} + +void tst_v4::subscriptions() +{ + { + QQmlComponent component(&engine, testFileUrl("subscriptions.1.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QObject *ro = qobject_cast<QObject *>(o); + QVERIFY(ro != 0); + + QCOMPARE(ro->property("targetHeight"), QVariant::fromValue<qreal>(201)); + + delete o; + } +} + +static QStringList messages; +static void msgHandler(QtMsgType, const char *msg) +{ + messages << QLatin1String(msg); +} + +static QByteArray getAddress(int address) +{ + return QByteArray::number(address); +} + +static QByteArray getLeading(int address) +{ + QByteArray leading; + if (address != -1) { + leading = getAddress(address); + leading.prepend(QByteArray(8 - leading.count(), ' ')); + } + return leading; +} + +#include <private/qv4instruction_p.h> +void tst_v4::debuggingDumpInstructions() +{ + QStringList expectedPreAddress; + expectedPreAddress << "\t\tNoop"; + expectedPreAddress << "\t0:0:"; + expectedPreAddress << "\t\tSubscribe\t\tObject_Reg(0) Notify_Signal(0) -> Subscribe_Slot(0)"; + expectedPreAddress << "\t\tSubscribeId\t\tId_Offset(0) -> Subscribe_Slot(0)"; + expectedPreAddress << "\t\tFetchAndSubscribe\tObject_Reg(0) Fast_Accessor(0x0) -> Output_Reg(0) Subscription_Slot(0)"; + expectedPreAddress << "\t\tLoadId\t\t\tId_Offset(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadScope\t\t-> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadRoot\t\t-> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadModuleObject\t\t) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadAttached\t\tObject_Reg(0) Attached_Index(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tUnaryNot\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tUnaryMinusNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tUnaryMinusInt\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tUnaryPlusNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tUnaryPlusInt\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertBoolToInt\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertBoolToNumber\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertBoolToString\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertIntToBool\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertIntToNumber\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertIntToString\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertNumberToBool\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertNumberToInt\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertNumberToString\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertStringToBool\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertStringToInt\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertStringToNumber\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertStringToUrl\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertStringToColor\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertUrlToBool\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertUrlToString\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertColorToBool\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertColorToString\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertObjectToBool\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tConvertNullToObject\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tResolveUrl\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathSinNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathCosNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathAbsNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathRoundNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathFloorNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathCeilNumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathPINumber\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadNull\t\tConstant(null) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadNumber\t\tConstant(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadInt\t\t\tConstant(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadBool\t\tConstant(false) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLoadString\t\tString_DataIndex(0) String_Length(0) -> Output_Register(0)"; + expectedPreAddress << "\t\tEnableV4Test\t\tString_DataIndex(0) String_Length(0)"; + expectedPreAddress << "\t\tTestV4Store\t\tInput_Reg(0) Reg_Type(0)"; + expectedPreAddress << "\t\tBitAndInt\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tBitOrInt\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tBitXorInt\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tAddNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tAddString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tSubNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMulNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tDivNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tModNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLShiftInt\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tRShiftInt\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tURShiftInt\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tGtNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLtNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tGeNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLeNumber\t\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tEqualNumber\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tNotEqualNumber\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStrictEqualNumber\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStrictNotEqualNumber\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tGtString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLtString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tGeString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tLeString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tEqualString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tNotEqualString\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStrictEqualString\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStrictNotEqualString\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tEqualObject\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tNotEqualObject\t\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStrictEqualObject\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStrictNotEqualObject\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathMaxNumber\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tMathMinNumber\tInput_Reg(0) Input_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tNewString\t\tRegister(0)"; + expectedPreAddress << "\t\tNewUrl\t\t\tRegister(0)"; + expectedPreAddress << "\t\tCleanupRegister\t\tRegister(0)"; + expectedPreAddress << "\t\tCopy\t\t\tInput_Reg(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tFetch\t\t\tObject_Reg(0) Property_Index(0) -> Output_Reg(0)"; + expectedPreAddress << "\t\tStore\t\t\tInput_Reg(0) -> Object_Reg(0) Property_Index(0)"; + expectedPreAddress << "\t\tJump\t\t\tAddress(UNIT_TEST_JUMP_ADDRESS) [if false == Input_Reg(0)]"; //(address + size() + i->jump.count) + expectedPreAddress << "\t\tBranchTrue\t\tAddress(UNIT_TEST_BRANCH_ADDRESS) [if true == Input_Reg(0)]"; //(address + size() + i->branchop.offset) + expectedPreAddress << "\t\tBranchFalse\t\tAddress(UNIT_TEST_BRANCH_ADDRESS) [if false == Input_Reg(0)]"; //(address + size() + i->branchop.offset) + expectedPreAddress << "\t\tBranch\t\t\tAddress(UNIT_TEST_BRANCH_ADDRESS)"; //(address + size() + i->branchop.offset) + expectedPreAddress << "\t\tBlock\t\t\tMask(0)"; + expectedPreAddress << "\t\tThrow\t\t\tInputReg(0)"; + expectedPreAddress << "\t\tInitString\t\tString_DataIndex(0) -> String_Slot(0)"; + QStringList expected; + + messages = QStringList(); + QtMsgHandler old = qInstallMsgHandler(msgHandler); + + QQmlJS::Bytecode bc; +#define DUMP_INSTR_IN_UNIT_TEST(I, FMT) { QQmlJS::V4InstrData<QQmlJS::V4Instr::I> i; memset(&i, 0, sizeof(i)); bc.append(i); } + FOR_EACH_V4_INSTR(DUMP_INSTR_IN_UNIT_TEST); +#undef DUMP_INSTR_IN_UNIT_TEST // NOTE: we memset in order to ensure stable output. + const char *start = bc.constData(); + const char *end = start + bc.size(); + const char *codeAddr = start; + int whichExpected = 0; +#define DUMP_INSTR_SIZE_IN_UNIT_TEST(I, FMT) { \ + QString currExpected = whichExpected < expectedPreAddress.size() ? expectedPreAddress.at(whichExpected++) : QString(); \ + currExpected.prepend(getLeading(codeAddr - start)); \ + expected.append(currExpected); \ + codeAddr += QQmlJS::V4Instr::size(static_cast<QQmlJS::V4Instr::Type>(QQmlJS::V4Instr::I)); \ + } + FOR_EACH_V4_INSTR(DUMP_INSTR_SIZE_IN_UNIT_TEST); +#undef DUMP_INSTR_SIZE_IN_UNIT_TEST // so that we generate the correct address for each instruction comparison + bc.dump(start, end); + + // ensure that the output was expected. + qInstallMsgHandler(old); + QCOMPARE(messages.count(), expected.count()); + for (int ii = 0; ii < messages.count(); ++ii) { + // Calculating the destination address of a null jump/branch instruction is tricky + // so instead we simply don't compare that part of those instructions. + QRegExp ignoreAddress("\\bAddress\\((\\w*)\\)"); + ignoreAddress.setMinimal(true); + QString expectOut = expected.at(ii); expectOut.replace(ignoreAddress, ""); + QString actualOut = messages.at(ii); actualOut.replace(ignoreAddress, ""); + QCOMPARE(actualOut, expectOut); + } +} + + QTEST_MAIN(tst_v4) #include "tst_v4.moc" diff --git a/tests/auto/qmltest/selftests/tst_datadriven.qml b/tests/auto/qmltest/selftests/tst_datadriven.qml new file mode 100644 index 0000000000..0686c12c00 --- /dev/null +++ b/tests/auto/qmltest/selftests/tst_datadriven.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 + +Item { + TestCase { + name:"data-driven-empty-init-data" + property int tests:0; + property int init_data_called_times:0; + function init_data() {init_data_called_times++;} + function initTestCase() {tests = 0; init_data_called_times = 0;} + function cleanupTestCase() {compare(tests, 2); compare(init_data_called_times, 2);} + + function test_test1() {tests++;} + function test_test2() {tests++;} + } + TestCase { + name:"data-driven-no-init-data" + property int tests:0; + function initTestCase() {tests = 0;} + function cleanupTestCase() {compare(tests, 2);} + + function test_test1() {tests++;} + function test_test2() {tests++;} + } + TestCase { + name:"data-driven-init-data" + property int tests:0; + property int init_data_called_times:0; + function initTestCase() {tests = 0; init_data_called_times = 0;} + function cleanupTestCase() {compare(tests, 2); compare(init_data_called_times, 1);} + function init_data() {init_data_called_times++; return [{tag:"data1", data:"test data 1"}];} + + function test_test1(row) {tests++; compare(row.data, "test data 1");} + function test_test2_data() {return [{tag:"data2", data:"test data 2"}]; } + function test_test2(row) {tests++; compare(row.data, "test data 2");} + } +} diff --git a/tests/auto/quick/examples/examples.pro b/tests/auto/quick/examples/examples.pro index c320fdad9e..d24fe80498 100644 --- a/tests/auto/quick/examples/examples.pro +++ b/tests/auto/quick/examples/examples.pro @@ -8,5 +8,3 @@ DEFINES += SRCDIR=\\\"$$PWD\\\" CONFIG += parallel_test #temporary QT += core-private gui-private qml-private quick-private v8-private testlib - -cross_compile: DEFINES += QTEST_CROSS_COMPILED diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 9a2a800cfd..b019c716b0 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -249,10 +249,6 @@ void tst_examples::sgexamples() { QFETCH(QString, file); -#if defined(QTEST_CROSS_COMPILED) - QSKIP("sources not available when cross compiled"); -#endif - QQmlComponent component(&engine, QUrl::fromLocalFile(file)); if (component.status() == QQmlComponent::Error) qWarning() << component.errors(); @@ -293,10 +289,6 @@ void tst_examples::sgsnippets() { QFETCH(QString, file); -#if defined(QTEST_CROSS_COMPILED) - QSKIP("sources not available when cross compiled"); -#endif - QQmlComponent component(&engine, QUrl::fromLocalFile(file)); if (component.status() == QQmlComponent::Error) qWarning() << component.errors(); diff --git a/tests/auto/quick/qquickaccessible/data/statictext.qml b/tests/auto/quick/qquickaccessible/data/statictext.qml index a0821cfc4d..7cf1b707a0 100644 --- a/tests/auto/quick/qquickaccessible/data/statictext.qml +++ b/tests/auto/quick/qquickaccessible/data/statictext.qml @@ -10,6 +10,10 @@ Item { width: 200 height: 50 text : "Hello Accessibility" + + // Setting any value of the attached property + // makes an item accessible. + Accessible.name: text } Text { diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index 48c07c6e2c..45a9d11c69 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -340,23 +340,25 @@ void tst_QQuickAccessible::hitTest() canvas->setSource(testFileUrl("hittest.qml")); canvas->show(); - QAI iface = QAI(QAccessible::queryAccessibleInterface(canvas)); - QVERIFY(iface.data()); - QAI rootItem = QAI(iface->child(0)); + QAI canvasIface = QAI(QAccessible::queryAccessibleInterface(canvas)); + QVERIFY(canvasIface.data()); + QAI rootItem = QAI(canvasIface->child(0)); QRect rootRect = rootItem->rect(); - // hit the root item - QAI itemHit(iface->childAt(rootRect.x() + 200, rootRect.y() + 50)); + // check the root item from app + QAI appIface = QAI(QAccessible::queryAccessibleInterface(qApp)); + QVERIFY(appIface); + QAI itemHit(appIface->childAt(rootRect.x() + 200, rootRect.y() + 50)); QVERIFY(itemHit); QCOMPARE(rootRect, itemHit->rect()); // hit rect1 - QAI rect1(rootItem->child(1)); + QAI rect1(rootItem->child(0)); QRect rect1Rect = rect1->rect(); - itemHit = QAI(rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10)); - QVERIFY(itemHit); - QCOMPARE(rect1Rect, itemHit->rect()); - QCOMPARE(itemHit->text(QAccessible::Name), QLatin1String("rect1")); + QAI rootItemIface = QAI(rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10)); + QVERIFY(rootItemIface); + QCOMPARE(rect1Rect, rootItemIface->rect()); + QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect1")); // should also work from top level (app) QAI app(QAccessible::queryAccessibleInterface(qApp)); @@ -366,16 +368,20 @@ void tst_QQuickAccessible::hitTest() QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1")); // hit rect201 - QAI rect2(rootItem->child(2)); - QAI rect20(rect2->child(1)); - QAI rect201(rect20->child(2)); + QAI rect2(rootItem->child(1)); + QVERIFY(rect2); + // FIXME: This is seems broken on mac + // QCOMPARE(rect2->rect().translated(rootItem->rect().x(), rootItem->rect().y()), QRect(0, 50, 100, 100)); + QAI rect20(rect2->child(0)); + QVERIFY(rect20); + QAI rect201(rect20->child(1)); QVERIFY(rect201); QRect rect201Rect = rect201->rect(); - itemHit = QAI(iface->childAt(rect201Rect.x() + 20, rect201Rect.y() + 20)); - QVERIFY(itemHit); - QCOMPARE(itemHit->rect(), rect201Rect); - QCOMPARE(itemHit->text(QAccessible::Name), QLatin1String("rect201")); + rootItemIface = QAI(canvasIface->childAt(rect201Rect.x() + 20, rect201Rect.y() + 20)); + QVERIFY(rootItemIface); + QCOMPARE(rootItemIface->rect(), rect201Rect); + QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201")); delete canvas; } diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml index c33901d294..10bc37e82d 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_arc.qml @@ -43,7 +43,10 @@ Canvas { ctx.moveTo(100, 0); ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true); ctx.fill(); + /*FIXME: from: http://www.w3.org/TR/2dcontext/#dom-context-2d-arc + If the anticlockwise argument is omitted or false and endAngle-startAngle is equal to or greater than 2Ï€, or, if the anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2Ï€, then the arc is the whole circumference of this circle. //verify(Helper.comparePixel(ctx,50,25, 0,255,0,255)); + */ } function test_angle_4() { var ctx = canvas.getContext('2d'); @@ -72,11 +75,10 @@ Canvas { ctx.moveTo(100, 0); ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false); ctx.fill(); - /*FIXME: - actual :[255,0,0,255] - expected:[0,255,0,255] +/- 0 - */ + /*FIXME: from: http://www.w3.org/TR/2dcontext/#dom-context-2d-arc + If the anticlockwise argument is omitted or false and endAngle-startAngle is equal to or greater than 2Ï€, or, if the anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2Ï€, then the arc is the whole circumference of this circle. //verify(Helper.comparePixel(ctx,50,25, 0,255,0,255)); + */ } function test_angle_6() { @@ -108,11 +110,7 @@ Canvas { ctx.beginPath(); ctx.arc(200, 25, 5, 0, 2*Math.PI, true); ctx.stroke(); - /*FIXME: - actual :[255,0,0,255] - expected:[0,255,0,255] +/- 0 - */ - //verify(Helper.comparePixel(ctx,50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx,50,25, 0,255,0,255)); } function test_nonempty() { var ctx = canvas.getContext('2d'); @@ -129,7 +127,6 @@ Canvas { verify(Helper.comparePixel(ctx,50,25, 0,255,0,255)); } function test_nonfinite() { - skip("FIXME"); var ctx = canvas.getContext('2d'); ctx.reset(); @@ -241,13 +238,13 @@ Canvas { verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); verify(Helper.comparePixel(ctx, 0,25, 0,255,0,255)); verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); } function test_scale_2() { @@ -271,7 +268,7 @@ Canvas { verify(Helper.comparePixel(ctx, 98,25, 0,255,0,255)); verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); verify(Helper.comparePixel(ctx, 50,48, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); } function test_selfintersect_1() { @@ -288,8 +285,8 @@ Canvas { ctx.beginPath(); ctx.arc(0, 0, 25, 0, -Math.PI/2, true); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); } function test_selfintersect_2() { @@ -307,10 +304,10 @@ Canvas { ctx.arc(100, 0, 25, 0, -Math.PI/2, true); ctx.stroke(); verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 90,10, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 97,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 97,2, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 97,3, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 90,10, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 97,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 97,2, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 97,3, 0,255,0,255)); verify(Helper.comparePixel(ctx, 2,48, 0,255,0,255)); } @@ -325,12 +322,12 @@ Canvas { ctx.beginPath(); ctx.arc(50, 50, 50, 0, Math.PI, false); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); verify(Helper.comparePixel(ctx, 20,48, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); } function test_shape_2() { @@ -362,11 +359,11 @@ Canvas { ctx.beginPath(); ctx.arc(0, 50, 50, 0, -Math.PI/2, false); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); } function test_shape_4() { @@ -398,11 +395,11 @@ Canvas { ctx.beginPath(); ctx.arc(300, 0, 100, 0, 5*Math.PI, false); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); } function test_twopie() { @@ -416,7 +413,7 @@ Canvas { ctx.beginPath(); ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -436,7 +433,8 @@ Canvas { ctx.beginPath(); ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true); ctx.stroke(); - verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); + //FIXME:still different behavior from browsers, > 2pi span issue + //verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -460,7 +458,7 @@ Canvas { ctx.beginPath(); ctx.arc(50, 25, 50, 0, 0, true); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#0f0'; @@ -470,7 +468,7 @@ Canvas { ctx.beginPath(); ctx.arc(50, 25, 50, 0, 0, false); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,20, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00' diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml index 84bfc1a8db..188d538d29 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_arcto.qml @@ -118,7 +118,7 @@ Canvas { ctx.beginPath(); ctx.arcTo(100, 50, 200, 50, 0.1); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -152,8 +152,6 @@ Canvas { var ctx = canvas.getContext('2d'); ctx.reset(); - skip("FIXME"); - ctx.moveTo(0, 0); ctx.lineTo(100, 0); ctx.arcTo(Infinity, 50, 0, 50, 0); @@ -221,7 +219,7 @@ Canvas { ctx.lineTo(-1000, 0); ctx.fill(); - skip("FIXME"); + //FIXME //verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); @@ -332,7 +330,8 @@ Canvas { ctx.arcTo(200, 25, 200, 50, 10); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); + //FIXME + //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); @@ -354,16 +353,15 @@ Canvas { ctx.lineTo(-100, 0); ctx.fill(); - skip("FIXME"); - //verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 0,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); } function test_zero() { var ctx = canvas.getContext('2d'); diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml index 23d7b719ff..a70c798594 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml @@ -271,6 +271,7 @@ Rectangle { ctx = c.getContext('2D'); verify(ctx); compare(ctx.canvas, c); + ignoreWarning(Qt.resolvedUrl("tst_canvas.qml") + ":10:9: QML Canvas: Canvas already initialized with a different context type"); ctx = c.getContext('invalid'); verify(!ctx); c.destroy(); diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml b/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml index bdc9d37663..60c63ae04b 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_composite.qml @@ -133,7 +133,6 @@ Canvas { } function test_solid() { - skip("FIXME"); var ctx = canvas.getContext('2d'); ctx.reset(); ctx.fillStyle = Qt.rgba(0, 1, 1, 1.0); @@ -230,7 +229,6 @@ Canvas { } function test_transparent() { - skip("FIXME"); var ctx = canvas.getContext('2d'); ctx.reset(); ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; @@ -279,7 +277,8 @@ Canvas { ctx.globalCompositeOperation = 'lighter'; ctx.fillStyle = 'rgba(0, 0, 255, 0.75)'; ctx.fillRect(0, 0, 100, 50); - verify(Helper.comparePixel(ctx, 50,25, 0,127,191,255, 5)); + //FIXME + //verify(Helper.comparePixel(ctx, 50,25, 0,127,191,255, 5)); ctx.reset(); ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; @@ -325,7 +324,6 @@ Canvas { } function test_uncovered() { - skip("FIXME"); var ctx = canvas.getContext('2d'); ctx.reset(); ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; @@ -334,7 +332,8 @@ Canvas { ctx.fillStyle = 'rgba(0, 0, 255, 0.75)'; ctx.translate(0, 25); ctx.fillRect(0, 50, 100, 50); - verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); + //FIXME + //verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); ctx.reset(); ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; @@ -343,7 +342,8 @@ Canvas { ctx.fillStyle = 'rgba(0, 0, 255, 0.75)'; ctx.translate(0, 25); ctx.fillRect(0, 50, 100, 50); - verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); + //FIXME + //verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); @@ -354,7 +354,8 @@ Canvas { ctx.fillStyle = 'rgba(0, 0, 255, 0.75)'; ctx.translate(0, 25); ctx.fillRect(0, 50, 100, 50); - verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); + //FIXME + //verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); ctx.reset(); ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; @@ -363,7 +364,8 @@ Canvas { ctx.fillStyle = 'rgba(0, 0, 255, 0.75)'; ctx.translate(0, 25); ctx.fillRect(0, 50, 100, 50); - verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); + //FIXME + //verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); ctx.reset(); ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; @@ -372,7 +374,8 @@ Canvas { ctx.fillStyle = 'rgba(0, 0, 255, 0.75)'; ctx.translate(0, 25); ctx.fillRect(0, 50, 100, 50); - verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); + //FIXME + //verify(Helper.comparePixel(ctx, 50,25, 0,0,0,0, 5)); } diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_path.qml b/tests/auto/quick/qquickcanvasitem/data/tst_path.qml index f72e5b9eaa..7c88106e5f 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_path.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_path.qml @@ -4,7 +4,6 @@ import "testhelper.js" as Helper Canvas { id:canvas; width:100;height:50; renderTarget: Canvas.Image; renderStrategy:Canvas.Threaded - smooth: false TestCase { name: "path"; when: windowShown @@ -102,7 +101,7 @@ Canvas { ctx.reset(); ctx.rect(20, 0, 20, 20); - //verify(ctx.isPointInPath(10, 10)); + verify(!ctx.isPointInPath(10, 10)); verify(ctx.isPointInPath(30, 10)); ctx.reset(); @@ -111,19 +110,19 @@ Canvas { verify(!ctx.isPointInPath(25, 30)); //verify(ctx.isPointInPath(30, 20)); verify(!ctx.isPointInPath(30, 30)); - //verify(!ctx.isPointInPath(40, 2)); + verify(!ctx.isPointInPath(40, 2)); //verify(ctx.isPointInPath(40, 20)); verify(!ctx.isPointInPath(40, 30)); verify(!ctx.isPointInPath(40, 47)); //verify(ctx.isPointInPath(45, 20)); - //verify(!ctx.isPointInPath(45, 30)); + verify(!ctx.isPointInPath(45, 30)); //verify(!ctx.isPointInPath(55, 20)); //verify(ctx.isPointInPath(55, 30)); - verify(!ctx.isPointInPath(60, 2)); + //verify(!ctx.isPointInPath(60, 2)); //verify(!ctx.isPointInPath(60, 20)); verify(ctx.isPointInPath(60, 30)); verify(!ctx.isPointInPath(60, 47)); - verify(!ctx.isPointInPath(70, 20)); + //verify(!ctx.isPointInPath(70, 20)); verify(ctx.isPointInPath(70, 30)); verify(!ctx.isPointInPath(75, 20)); verify(!ctx.isPointInPath(75, 30)); @@ -131,8 +130,8 @@ Canvas { ctx.reset(); ctx.arc(50, 25, 10, 0, 7, false); verify(!ctx.isPointInPath(50, 10)); - //verify(ctx.isPointInPath(50, 20)); - //verify(ctx.isPointInPath(50, 30)); + verify(ctx.isPointInPath(50, 20)); + verify(ctx.isPointInPath(50, 30)); verify(!ctx.isPointInPath(50, 40)); verify(!ctx.isPointInPath(30, 20)); verify(!ctx.isPointInPath(70, 20)); @@ -143,16 +142,16 @@ Canvas { ctx.rect(0, 0, 20, 20); verify(ctx.isPointInPath(0, 0)); verify(ctx.isPointInPath(10, 0)); - //verify(ctx.isPointInPath(20, 0)); - //verify(ctx.isPointInPath(20, 10)); - //verify(ctx.isPointInPath(20, 20)); - //verify(ctx.isPointInPath(10, 20)); - //verify(ctx.isPointInPath(0, 20)); + verify(ctx.isPointInPath(20, 0)); + verify(ctx.isPointInPath(20, 10)); + verify(ctx.isPointInPath(20, 20)); + verify(ctx.isPointInPath(10, 20)); + verify(ctx.isPointInPath(0, 20)); verify(ctx.isPointInPath(0, 10)); verify(!ctx.isPointInPath(10, -0.01)); verify(!ctx.isPointInPath(10, 20.01)); verify(!ctx.isPointInPath(-0.01, 10)); - //verify(!ctx.isPointInPath(20.01, 10)); + verify(!ctx.isPointInPath(20.01, 10)); ctx.reset(); verify(!ctx.isPointInPath(0, 0)); @@ -160,13 +159,13 @@ Canvas { ctx.reset(); ctx.rect(-100, -50, 200, 100); - //verify(ctx.isPointInPath(Infinity, 0)); - //verify(ctx.isPointInPath(-Infinity, 0)); - //verify(ctx.isPointInPath(NaN, 0)); - //verify(ctx.isPointInPath(0, Infinity)); - //verify(ctx.isPointInPath(0, -Infinity)); - //verify(ctx.isPointInPath(0, NaN)); - //verify(ctx.isPointInPath(NaN, NaN)); + verify(!ctx.isPointInPath(Infinity, 0)); + verify(!ctx.isPointInPath(-Infinity, 0)); + verify(!ctx.isPointInPath(NaN, 0)); + verify(!ctx.isPointInPath(0, Infinity)); + verify(!ctx.isPointInPath(0, -Infinity)); + verify(!ctx.isPointInPath(0, NaN)); + verify(!ctx.isPointInPath(NaN, NaN)); ctx.reset(); ctx.rect(0, -100, 20, 20); @@ -174,9 +173,9 @@ Canvas { verify(!ctx.isPointInPath(10, -110)); verify(ctx.isPointInPath(10, -90)); verify(!ctx.isPointInPath(10, -70)); - //verify(!ctx.isPointInPath(30, -20)); - //verify(ctx.isPointInPath(30, 0)); - //verify(!ctx.isPointInPath(30, 20)); + verify(!ctx.isPointInPath(30, -20)); + verify(ctx.isPointInPath(30, 0)); + verify(!ctx.isPointInPath(30, 20)); ctx.reset(); ctx.rect(0, 0, 20, 20); @@ -193,7 +192,7 @@ Canvas { ctx.rect(0, 0, 20, 20); verify(!ctx.isPointInPath(-40, 10)); verify(!ctx.isPointInPath(10, 10)); - //verify(!ctx.isPointInPath(49, 10)); + verify(!ctx.isPointInPath(49, 10)); verify(ctx.isPointInPath(51, 10)); verify(ctx.isPointInPath(69, 10)); verify(!ctx.isPointInPath(71, 10)); @@ -203,7 +202,7 @@ Canvas { ctx.translate(50, 0); verify(!ctx.isPointInPath(-40, 10)); verify(!ctx.isPointInPath(10, 10)); - //verify(!ctx.isPointInPath(49, 10)); + verify(!ctx.isPointInPath(49, 10)); verify(ctx.isPointInPath(51, 10)); verify(ctx.isPointInPath(69, 10)); verify(!ctx.isPointInPath(71, 10)); @@ -213,7 +212,7 @@ Canvas { ctx.rect(-70, 0, 20, 20); verify(!ctx.isPointInPath(-40, 10)); verify(!ctx.isPointInPath(10, 10)); - //verify(!ctx.isPointInPath(49, 10)); + verify(!ctx.isPointInPath(49, 10)); verify(ctx.isPointInPath(51, 10)); verify(ctx.isPointInPath(69, 10)); verify(!ctx.isPointInPath(71, 10)); @@ -224,7 +223,7 @@ Canvas { ctx.lineTo(20, 20); ctx.lineTo(0, 20); verify(ctx.isPointInPath(10, 10)); - //verify(!ctx.isPointInPath(30, 10)); + verify(!ctx.isPointInPath(30, 10)); ctx.reset(); ctx.moveTo(0, 0); @@ -279,8 +278,8 @@ Canvas { ctx.fillStyle = '#0f0'; ctx.fill(); - //verify(Helper.comparePixel(ctx, 90,10, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 10,40, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 90,10, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 10,40, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#000'; @@ -292,7 +291,7 @@ Canvas { ctx.rect(10, 10, 80, 30); ctx.fill(); - //verify(Helper.comparePixel(ctx, 50,25, 0,127,0,255, 1)); + verify(Helper.comparePixel(ctx, 50,25, 0,127,0,255, 1)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -310,7 +309,7 @@ Canvas { ctx.lineTo(0, 50); ctx.fill(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#0f0'; @@ -422,7 +421,7 @@ Canvas { ctx.arc(50, 25, 10, 0, 0, false); ctx.stroke(); - // verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); @@ -457,7 +456,8 @@ Canvas { ctx.lineTo(-100, 1000); ctx.stroke(); - verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + //FIXME:lineJoin with miterLimit test fail! + //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#0f0'; @@ -564,15 +564,15 @@ Canvas { ctx.stroke(); ctx.restore(); - //verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 0,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -611,14 +611,14 @@ Canvas { ctx.restore(); //verify(Helper.comparePixel(ctx, 0,0, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,0, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 99,0, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 0,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,25, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 0,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,49, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 99,49, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -847,7 +847,7 @@ Canvas { ctx.beginPath(); ctx.lineTo(100, 50); ctx.stroke(); - // verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -911,8 +911,8 @@ Canvas { ctx.beginPath(); ctx.bezierCurveTo(100, 50, 200, 50, 200, 50); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 95,45, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 95,45, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -923,7 +923,7 @@ Canvas { ctx.bezierCurveTo(0, 25, 100, 25, 100, 25); ctx.stroke(); verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 5,45, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 5,45, 0,255,0,255)); ctx.reset(); ctx.moveTo(0, 0); @@ -1035,11 +1035,11 @@ Canvas { ctx.moveTo(-2, 3.1); ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -1078,8 +1078,8 @@ Canvas { ctx.beginPath(); ctx.quadraticCurveTo(100, 50, 200, 50); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 95,45, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 95,45, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -1090,7 +1090,7 @@ Canvas { ctx.quadraticCurveTo(0, 25, 100, 25); ctx.stroke(); verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 5,45, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 5,45, 0,255,0,255)); ctx.reset(); ctx.moveTo(0, 0); @@ -1135,11 +1135,11 @@ Canvas { ctx.moveTo(-1, 1.05); ctx.quadraticCurveTo(0, -1, 1.2, 1.05); ctx.stroke(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 1,1, 0,255,0,255)); //verify(Helper.comparePixel(ctx, 98,1, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 1,48, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 98,48, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -1260,8 +1260,8 @@ Canvas { ctx.lineTo(0, 50); ctx.fillStyle = '#0f0'; ctx.fill(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); - //verify(Helper.comparePixel(ctx, 90,45, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 90,45, 0,255,0,255)); ctx.reset(); @@ -1399,7 +1399,7 @@ Canvas { ctx.fillStyle = '#0f0'; ctx.fill(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#f00'; @@ -1416,7 +1416,7 @@ Canvas { ctx.rotate(Math.PI/2); ctx.scale(0.1, 0.1); ctx.fill(); - //verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); + verify(Helper.comparePixel(ctx, 50,25, 0,255,0,255)); ctx.reset(); ctx.fillStyle = '#0f0'; diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 2ed42e7f0f..f926dbe8f0 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -443,11 +443,14 @@ void tst_qquickflickable::movingAndDragging() // Vertical with a quick press-move-release: should cause a flick in release. QSignalSpy vFlickSpy(flickable, SIGNAL(flickingVerticallyChanged())); - - QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50, 90)); - QTest::qWait(10); - QTest::mouseMove(canvas, QPoint(50, 40)); - QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 40)); + // Use something that generates a huge velocity just to make it testable. + // In practice this feature matters on touchscreen devices where the + // underlying drivers will hopefully provide a pre-calculated velocity + // (based on more data than what the UI gets), thus making this use case + // working even with small movements. + QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mouseMove(canvas, QPoint(50, 300), 10); + QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(50, 100), 10); QCOMPARE(vFlickSpy.count(), 1); diff --git a/tests/auto/quick/qquickflipable/data/flip-flipable.qml b/tests/auto/quick/qquickflipable/data/flip-flipable.qml new file mode 100644 index 0000000000..4f22a0df6d --- /dev/null +++ b/tests/auto/quick/qquickflipable/data/flip-flipable.qml @@ -0,0 +1,28 @@ +import QtQuick 2.0 + +Flipable { + id: flipable + width: 640; height: 480 + property bool flipped: false + + front: Rectangle { color: "red"; anchors.fill: flipable } + back: Rectangle { color: "blue"; anchors.fill: flipable } + + transform: Rotation { + id: rotation + origin.x: flipable.width/2 + origin.y: flipable.height/2 + axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis + angle: 0 // the default angle + } + + states: State { + name: "back" + PropertyChanges { target: rotation; angle: 540 } + when: flipable.flipped + } + + transitions: Transition { + NumberAnimation { target: rotation; property: "angle"; duration: 500 } + } +} diff --git a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp index 4fe155168a..8c1c248925 100644 --- a/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp +++ b/tests/auto/quick/qquickflipable/tst_qquickflipable.cpp @@ -58,6 +58,7 @@ private slots: void create(); void checkFrontAndBack(); void setFrontAndBack(); + void flipFlipable(); // below here task issues void QTBUG_9161_crash(); @@ -109,6 +110,20 @@ void tst_qquickflipable::setFrontAndBack() delete obj; } +void tst_qquickflipable::flipFlipable() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("flip-flipable.qml")); + QQuickFlipable *obj = qobject_cast<QQuickFlipable*>(c.create()); + QVERIFY(obj != 0); + QVERIFY(obj->side() == QQuickFlipable::Front); + obj->setProperty("flipped", QVariant(true)); + QTRY_VERIFY(obj->side() == QQuickFlipable::Back); + QTRY_VERIFY(obj->side() == QQuickFlipable::Front); + QTRY_VERIFY(obj->side() == QQuickFlipable::Back); + delete obj; +} + void tst_qquickflipable::QTBUG_9161_crash() { QQuickView *canvas = new QQuickView; diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index c7b5ca6b40..22c9004ccc 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -2094,15 +2094,17 @@ void tst_QQuickGridView::componentChanges() QTRY_VERIFY(gridView); QQmlComponent component(canvas->engine()); - component.setData("import QtQuick 1.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile("")); + component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile("")); QQmlComponent delegateComponent(canvas->engine()); - delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile("")); + delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile("")); QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged())); QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged())); QSignalSpy headerSpy(gridView, SIGNAL(headerChanged())); QSignalSpy footerSpy(gridView, SIGNAL(footerChanged())); + QSignalSpy headerItemSpy(gridView, SIGNAL(headerItemChanged())); + QSignalSpy footerItemSpy(gridView, SIGNAL(footerItemChanged())); gridView->setHighlight(&component); gridView->setDelegate(&delegateComponent); @@ -2114,10 +2116,15 @@ void tst_QQuickGridView::componentChanges() QTRY_COMPARE(gridView->header(), &component); QTRY_COMPARE(gridView->footer(), &component); + QVERIFY(gridView->headerItem()); + QVERIFY(gridView->footerItem()); + QTRY_COMPARE(highlightSpy.count(),1); QTRY_COMPARE(delegateSpy.count(),1); QTRY_COMPARE(headerSpy.count(),1); QTRY_COMPARE(footerSpy.count(),1); + QTRY_COMPARE(headerItemSpy.count(),1); + QTRY_COMPARE(footerItemSpy.count(),1); gridView->setHighlight(&component); gridView->setDelegate(&delegateComponent); @@ -2128,6 +2135,8 @@ void tst_QQuickGridView::componentChanges() QTRY_COMPARE(delegateSpy.count(),1); QTRY_COMPARE(headerSpy.count(),1); QTRY_COMPARE(footerSpy.count(),1); + QTRY_COMPARE(headerItemSpy.count(),1); + QTRY_COMPARE(footerItemSpy.count(),1); delete canvas; } diff --git a/tests/auto/quick/qquickitem/data/focusSubItemInNonFocusScope.qml b/tests/auto/quick/qquickitem/data/focusSubItemInNonFocusScope.qml new file mode 100644 index 0000000000..0e50710717 --- /dev/null +++ b/tests/auto/quick/qquickitem/data/focusSubItemInNonFocusScope.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +Rectangle { + width: 400; height: 400 + + FocusScope { + width: 400; height: 400 + focus: true + Item { + width: 400; height: 400 + Item { + id: dummy + objectName: "dummyItem" + focus: true + } + TextInput { + id: ti + objectName: "textInput" + focus: true + } + } + } +} diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index abd0da8ac1..a1377694e4 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -139,6 +139,9 @@ private slots: void addedToCanvas(); void changeParent(); void multipleFocusClears(); + void focusSubItemInNonFocusScope(); + void parentItemWithFocus(); + void reparentFocusedItem(); void constructor(); void setParentItem(); @@ -707,6 +710,7 @@ void tst_qquickitem::changeParent() focusState[item].set(true, true); focusState.active(item); FVERIFY(); + delete child2; } } @@ -720,6 +724,133 @@ void tst_qquickitem::multipleFocusClears() QTRY_VERIFY(QGuiApplication::focusWindow() == view); } +void tst_qquickitem::focusSubItemInNonFocusScope() +{ + QQuickView *view = new QQuickView; + view->setSource(testFileUrl("focusSubItemInNonFocusScope.qml")); + view->show(); + qApp->processEvents(); + + QQuickItem *dummyItem = view->rootObject()->findChild<QQuickItem *>("dummyItem"); + QVERIFY(dummyItem); + + QQuickItem *textInput = view->rootObject()->findChild<QQuickItem *>("textInput"); + QVERIFY(textInput); + + QVERIFY(dummyItem->hasFocus()); + QVERIFY(!textInput->hasFocus()); + QVERIFY(dummyItem->hasActiveFocus()); + + QVERIFY(QMetaObject::invokeMethod(textInput, "forceActiveFocus")); + + QVERIFY(!dummyItem->hasFocus()); + QVERIFY(textInput->hasFocus()); + QVERIFY(textInput->hasActiveFocus()); + + delete view; +} + +void tst_qquickitem::parentItemWithFocus() +{ + QQuickCanvas canvas; + ensureFocus(&canvas); + QTRY_VERIFY(QGuiApplication::focusWindow() == &canvas); + { + QQuickItem parent; + QQuickItem child; + + FocusState focusState; + focusState << &parent << &child; + FVERIFY(); + + parent.setFocus(true); + child.setFocus(true); + focusState[&parent].set(true, false); + focusState[&child].set(true, false); + FVERIFY(); + + child.setParentItem(&parent); + focusState[&parent].set(true, false); + focusState[&child].set(false, false); + FVERIFY(); + + parent.setParentItem(canvas.rootItem()); + focusState[&parent].set(true, true); + focusState[&child].set(false, false); + focusState.active(&parent); + FVERIFY(); + + child.forceActiveFocus(); + focusState[&parent].set(false, false); + focusState[&child].set(true, true); + focusState.active(&child); + FVERIFY(); + } { + QQuickItem parent; + QQuickItem child; + QQuickItem grandchild(&child); + + FocusState focusState; + focusState << &parent << &child << &grandchild; + FVERIFY(); + + parent.setFocus(true); + grandchild.setFocus(true); + focusState[&parent].set(true, false); + focusState[&child].set(false, false); + focusState[&grandchild].set(true, false); + FVERIFY(); + + child.setParentItem(&parent); + focusState[&parent].set(true, false); + focusState[&child].set(false, false); + focusState[&grandchild].set(false, false); + FVERIFY(); + + parent.setParentItem(canvas.rootItem()); + focusState[&parent].set(true, true); + focusState[&child].set(false, false); + focusState[&grandchild].set(false, false); + focusState.active(&parent); + FVERIFY(); + + grandchild.forceActiveFocus(); + focusState[&parent].set(false, false); + focusState[&child].set(false, false); + focusState[&grandchild].set(true, true); + focusState.active(&grandchild); + FVERIFY(); + } +} + +void tst_qquickitem::reparentFocusedItem() +{ + QQuickCanvas canvas; + ensureFocus(&canvas); + QTRY_VERIFY(QGuiApplication::focusWindow() == &canvas); + + QQuickItem parent(canvas.rootItem()); + QQuickItem child(&parent); + QQuickItem sibling(&parent); + QQuickItem grandchild(&child); + + FocusState focusState; + focusState << &parent << &child << &sibling << &grandchild; + FVERIFY(); + + grandchild.setFocus(true); + focusState[&parent].set(false, false); + focusState[&child].set(false, false); + focusState[&sibling].set(false, false); + focusState[&grandchild].set(true, true); + focusState.active(&grandchild); + FVERIFY(); + + // Parenting the item to another item within the same focus scope shouldn't change it's focus. + child.setParentItem(&sibling); + FVERIFY(); +} + void tst_qquickitem::constructor() { QQuickItem *root = new QQuickItem; diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 1218d3cfcb..1ef895d111 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -2120,13 +2120,13 @@ void tst_QQuickListView::sectionsPositioning() QVERIFY(bottomItem); QCOMPARE(bottomItem->y(), 380.); - // Change current section + // Change current section, and verify case insensitive comparison listview->setContentY(10); model.modifyItem(0, "One", "aaa"); - model.modifyItem(1, "Two", "aaa"); - model.modifyItem(2, "Three", "aaa"); - model.modifyItem(3, "Four", "aaa"); - model.modifyItem(4, "Five", "aaa"); + model.modifyItem(1, "Two", "AAA"); + model.modifyItem(2, "Three", "aAa"); + model.modifyItem(3, "Four", "aaA"); + model.modifyItem(4, "Five", "Aaa"); QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); QTRY_COMPARE(listview->currentSection(), QString("aaa")); diff --git a/tests/auto/quick/qquickloader/data/RedRect.qml b/tests/auto/quick/qquickloader/data/RedRect.qml new file mode 100644 index 0000000000..0eec9b56b7 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/RedRect.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +Rectangle { + objectName: "red" + width: 100 + height: 100 + color: "red" +} diff --git a/tests/auto/quick/qquickloader/data/implicitSize.qml b/tests/auto/quick/qquickloader/data/implicitSize.qml index 5c8c8348ed..ae8c0b8b30 100644 --- a/tests/auto/quick/qquickloader/data/implicitSize.qml +++ b/tests/auto/quick/qquickloader/data/implicitSize.qml @@ -3,12 +3,17 @@ import QtQuick 2.0 Rectangle { property real implWidth: 0 property real implHeight: 0 + function changeImplicitSize () { + loader.item.implicitWidth = 200 + loader.item.implicitHeight = 300 + } color: "green" width: loader.implicitWidth+50 height: loader.implicitHeight+50 Loader { id: loader + objectName: "loader" sourceComponent: Item { anchors.centerIn: parent diff --git a/tests/auto/quick/qquickloader/data/loadedSignal.2.qml b/tests/auto/quick/qquickloader/data/loadedSignal.2.qml new file mode 100644 index 0000000000..a4a663c71f --- /dev/null +++ b/tests/auto/quick/qquickloader/data/loadedSignal.2.qml @@ -0,0 +1,31 @@ +import QtQuick 2.0 + +Item { + id: root + + width: 200 + height: 200 + + property bool success: true + property int loadCount: 0 + + Loader { + id: loader + anchors.fill: parent + asynchronous: true + active: false + source: "TestComponent.qml" + onLoaded: { + if (status !== Loader.Ready) { + root.success = false; + } + root.loadCount++; + } + } + + function triggerLoading() { + // we set source to a valid path (but which is an invalid / erroneous component) + // we should not get onLoaded, since the status should not be Ready. + loader.source = "GreenRect.qml" // causes reference error. + } +} diff --git a/tests/auto/quick/qquickloader/data/loadedSignal.qml b/tests/auto/quick/qquickloader/data/loadedSignal.qml new file mode 100644 index 0000000000..7cc0fed001 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/loadedSignal.qml @@ -0,0 +1,48 @@ +import QtQuick 2.0 + +Item { + id: root + + width: 200 + height: 200 + + property bool success: true + property int loadCount: 0 + + Loader { + id: loader + anchors.fill: parent + asynchronous: true + active: false + source: "TestComponent.qml" + onLoaded: { + if (status !== Loader.Ready) { + root.success = false; + } + root.loadCount++; + } + } + + function triggerLoading() { + // we set active to true, which triggers loading. + // we then immediately set active to false. + // this should clear the incubator and stop loading. + loader.active = true; + loader.active = false; + } + + function activate() { + loader.active = true; + } + + function deactivate() { + loader.active = false; + } + + function triggerMultipleLoad() { + loader.active = false; // deactivate as a precondition. + loader.source = "BlueRect.qml" + loader.active = true; // should trigger loading to begin + loader.source = "RedRect.qml"; // should clear the incubator and restart loading + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 01781f7b54..3bb06f737b 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -92,7 +92,7 @@ private slots: void noResize(); void networkRequestUrl(); void failNetworkRequest(); -// void networkComponent(); + void networkComponent(); void active(); void initialPropertyValues_data(); void initialPropertyValues(); @@ -111,6 +111,7 @@ private slots: void asynchronous(); void asynchronous_clear(); void simultaneousSyncAsync(); + void loadedSignal(); void parented(); void sizeBound(); @@ -442,21 +443,21 @@ void tst_QQuickLoader::networkRequestUrl() delete loader; } -/* XXX Component waits until all dependencies are loaded. Is this actually possible? +/* XXX Component waits until all dependencies are loaded. Is this actually possible? */ void tst_QQuickLoader::networkComponent() { TestHTTPServer server(SERVER_PORT); QVERIFY(server.isValid()); - server.serveDirectory("slowdata", TestHTTPServer::Delay); + server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQmlComponent component(&engine); component.setData(QByteArray( "import QtQuick 2.0\n" "import \"http://127.0.0.1:14450/\" as NW\n" "Item {\n" - " Component { id: comp; NW.SlowRect {} }\n" + " Component { id: comp; NW.Rect120x60 {} }\n" " Loader { sourceComponent: comp } }") - , dataDirectoryUrl()); + , dataDirectory()); QQuickItem *item = qobject_cast<QQuickItem*>(component.create()); QVERIFY(item); @@ -472,7 +473,6 @@ void tst_QQuickLoader::networkComponent() delete loader; } -*/ void tst_QQuickLoader::failNetworkRequest() { @@ -842,6 +842,18 @@ void tst_QQuickLoader::implicitSize() QCOMPARE(item->property("implHeight").toReal(), 100.); QCOMPARE(item->property("implWidth").toReal(), 100.); + QQuickLoader *loader = item->findChild<QQuickLoader*>("loader"); + QSignalSpy implWidthSpy(loader, SIGNAL(implicitWidthChanged())); + QSignalSpy implHeightSpy(loader, SIGNAL(implicitHeightChanged())); + + QMetaObject::invokeMethod(item, "changeImplicitSize"); + + QCOMPARE(loader->property("implicitWidth").toReal(), 200.); + QCOMPARE(loader->property("implicitHeight").toReal(), 300.); + + QCOMPARE(implWidthSpy.count(), 1); + QCOMPARE(implHeightSpy.count(), 1); + delete item; } @@ -988,6 +1000,47 @@ void tst_QQuickLoader::simultaneousSyncAsync() delete root; } +void tst_QQuickLoader::loadedSignal() +{ + { + // ensure that triggering loading (by setting active = true) + // and then immediately setting active to false, causes the + // loader to be deactivated, including disabling the incubator. + QQmlComponent component(&engine, testFileUrl("loadedSignal.qml")); + QObject *obj = component.create(); + + QMetaObject::invokeMethod(obj, "triggerLoading"); + QTest::qWait(100); // ensure that loading would have finished if it wasn't deactivated + QCOMPARE(obj->property("loadCount").toInt(), 0); + QVERIFY(obj->property("success").toBool()); + + QMetaObject::invokeMethod(obj, "triggerLoading"); + QTest::qWait(100); + QCOMPARE(obj->property("loadCount").toInt(), 0); + QVERIFY(obj->property("success").toBool()); + + QMetaObject::invokeMethod(obj, "triggerMultipleLoad"); + QTest::qWait(100); + QCOMPARE(obj->property("loadCount").toInt(), 1); // only one loaded signal should be emitted. + QVERIFY(obj->property("success").toBool()); + + delete obj; + } + + { + // ensure that an error doesn't result in the onLoaded signal being emitted. + QQmlComponent component(&engine, testFileUrl("loadedSignal.2.qml")); + QObject *obj = component.create(); + + QMetaObject::invokeMethod(obj, "triggerLoading"); + QTest::qWait(100); + QCOMPARE(obj->property("loadCount").toInt(), 0); + QVERIFY(obj->property("success").toBool()); + + delete obj; + } +} + void tst_QQuickLoader::parented() { QQmlComponent component(&engine, testFileUrl("parented.qml")); diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index a4b04bb88a..f072b005bc 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -796,13 +796,11 @@ void tst_QQuickMouseArea::hoverVisible() mouseTracker->setVisible(true); - QTest::mouseMove(canvas,QPoint(10,31)); - QCOMPARE(mouseTracker->hovered(), true); QCOMPARE(enteredSpy.count(), 1); QEXPECT_FAIL("", "QTBUG-24282", Continue); - QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(10,32)); + QCOMPARE(QPointF(mouseTracker->mouseX(), mouseTracker->mouseY()), QPointF(11,33)); delete canvas; } diff --git a/tests/auto/quick/qquickpathview/data/panels.qml b/tests/auto/quick/qquickpathview/data/panels.qml new file mode 100644 index 0000000000..a111e45736 --- /dev/null +++ b/tests/auto/quick/qquickpathview/data/panels.qml @@ -0,0 +1,44 @@ +import QtQuick 2.0 + +Item { + id: root + property bool snapOne: false + property bool enforceRange: false + width: 320; height: 480 + + VisualItemModel { + id: itemModel + + Rectangle { + width: root.width + height: root.height + color: "blue" + } + Rectangle { + width: root.width + height: root.height + color: "yellow" + } + Rectangle { + width: root.width + height: root.height + color: "green" + } + } + + PathView { + id: view + objectName: "view" + anchors.fill: parent + model: itemModel + preferredHighlightBegin: 0.5 + preferredHighlightEnd: 0.5 + flickDeceleration: 30 + highlightRangeMode: enforceRange ? PathView.StrictlyEnforceRange : PathView.NoHighlightRange + snapMode: root.snapOne ? PathView.SnapOneItem : PathView.SnapToItem + path: Path { + startX: -root.width; startY: root.height/2 + PathLine { x: root.width*2; y: root.height/2 } + } + } +} diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 85d2c3b6ea..fbe96bf672 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -125,6 +125,10 @@ private slots: void asynchronous(); void cancelDrag(); void maximumFlickVelocity(); + void snapToItem(); + void snapToItem_data(); + void snapOneItem(); + void snapOneItem_data(); }; class TestObject : public QObject @@ -1541,6 +1545,89 @@ void tst_QQuickPathView::maximumFlickVelocity() delete canvas; } +void tst_QQuickPathView::snapToItem() +{ + QFETCH(bool, enforceRange); + + QQuickView *canvas = createView(); + canvas->setSource(testFileUrl("panels.qml")); + QQuickPathView *pathview = canvas->rootObject()->findChild<QQuickPathView*>("view"); + QVERIFY(pathview != 0); + + canvas->rootObject()->setProperty("enforceRange", enforceRange); + QTRY_VERIFY(!pathview->isMoving()); // ensure stable + + int currentIndex = pathview->currentIndex(); + + QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged())); + + flick(canvas, QPoint(200,10), QPoint(10,10), 180); + + QVERIFY(pathview->isMoving()); + QTRY_VERIFY(!pathview->isMoving()); + + QVERIFY(pathview->offset() == qFloor(pathview->offset())); + + if (enforceRange) + QVERIFY(pathview->currentIndex() != currentIndex); + else + QVERIFY(pathview->currentIndex() == currentIndex); +} + +void tst_QQuickPathView::snapToItem_data() +{ + QTest::addColumn<bool>("enforceRange"); + + QTest::newRow("no enforce range") << false; + QTest::newRow("enforce range") << true; +} + +void tst_QQuickPathView::snapOneItem() +{ + QFETCH(bool, enforceRange); + + QQuickView *canvas = createView(); + canvas->setSource(testFileUrl("panels.qml")); + canvas->show(); + canvas->requestActivateWindow(); + QTest::qWaitForWindowShown(canvas); + QTRY_COMPARE(canvas, qGuiApp->focusWindow()); + + QQuickPathView *pathview = canvas->rootObject()->findChild<QQuickPathView*>("view"); + QVERIFY(pathview != 0); + + canvas->rootObject()->setProperty("enforceRange", enforceRange); + + QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged())); + + canvas->rootObject()->setProperty("snapOne", true); + QVERIFY(snapModeSpy.count() == 1); + QTRY_VERIFY(!pathview->isMoving()); // ensure stable + + int currentIndex = pathview->currentIndex(); + + double startOffset = pathview->offset(); + flick(canvas, QPoint(200,10), QPoint(10,10), 180); + + QVERIFY(pathview->isMoving()); + QTRY_VERIFY(!pathview->isMoving()); + + // must have moved only one item + QCOMPARE(pathview->offset(), fmodf(3.0 + startOffset - 1.0, 3.0)); + + if (enforceRange) + QVERIFY(pathview->currentIndex() == currentIndex+1); + else + QVERIFY(pathview->currentIndex() == currentIndex); +} + +void tst_QQuickPathView::snapOneItem_data() +{ + QTest::addColumn<bool>("enforceRange"); + + QTest::newRow("no enforce range") << false; + QTest::newRow("enforce range") << true; +} QTEST_MAIN(tst_QQuickPathView) diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index ab24fbe995..ae2bdc7fae 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -636,7 +636,7 @@ void tst_qquickpositioners::moveTransitions(const QString &positionerObjectName) QQuickView *canvas = QQuickViewTestUtil::createView(); QQmlContext *ctxt = canvas->rootContext(); - ctxt->setContextProperty("enableAddTransition", false); + ctxt->setContextProperty("enableAddTransition", QVariant(false)); ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom); ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia); ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom); diff --git a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp index 00ae8fc76d..1edf511ebf 100644 --- a/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp +++ b/tests/auto/quick/qquickshadereffect/tst_qquickshadereffect.cpp @@ -257,13 +257,12 @@ void tst_qquickshadereffect::lookThroughShaderCode() if ((presenceFlags & TexCoordPresent) == 0) expected += "Warning: Missing reference to \'qt_MultiTexCoord0\'.\n"; if ((presenceFlags & MatrixPresent) == 0) - expected += "Warning: Missing reference to \'qt_Matrix\'.\n"; + expected += "Warning: Vertex shader is missing reference to \'qt_Matrix\'.\n"; if ((presenceFlags & OpacityPresent) == 0) - expected += "Warning: Missing reference to \'qt_Opacity\'.\n"; + expected += "Warning: Shaders are missing reference to \'qt_Opacity\'.\n"; item.setVertexShader(vertexShader); item.setFragmentShader(fragmentShader); - item.ensureCompleted(); QCOMPARE(item.parseLog(), expected); // If the uniform was successfully parsed, the notify signal has been connected to an update slot. diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index f4ba0864e5..724b24280a 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -1654,7 +1654,7 @@ void tst_qquicktext::implicitSizeBinding() QFETCH(QString, format); QString componentStr = "import QtQuick 2.0\nText { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }"; - QDeclarativeComponent textComponent(&engine); + QQmlComponent textComponent(&engine); textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QScopedPointer<QObject> object(textComponent.create()); QQuickText *textObject = qobject_cast<QQuickText *>(object.data()); diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index ab21f3bc54..dd9aa0acad 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -1999,15 +1999,16 @@ void tst_qquicktextedit::cursorDelegate() void tst_qquicktextedit::cursorVisible() { + QQuickTextEdit edit; + edit.componentComplete(); + QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool))); + QQuickView view(testFileUrl("cursorVisible.qml")); view.show(); view.requestActivateWindow(); QTest::qWaitForWindowShown(&view); QTRY_COMPARE(&view, qGuiApp->focusWindow()); - QQuickTextEdit edit; - QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool))); - QCOMPARE(edit.isCursorVisible(), false); edit.setCursorVisible(true); @@ -2034,7 +2035,7 @@ void tst_qquicktextedit::cursorVisible() QCOMPARE(edit.isCursorVisible(), true); QCOMPARE(spy.count(), 5); - QQuickView alternateView; + QWindow alternateView; alternateView.show(); alternateView.requestActivateWindow(); QTest::qWaitForWindowShown(&alternateView); @@ -2046,6 +2047,47 @@ void tst_qquicktextedit::cursorVisible() QTest::qWaitForWindowShown(&view); QCOMPARE(edit.isCursorVisible(), true); QCOMPARE(spy.count(), 7); + + { // Cursor attribute with 0 length hides cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 8); + + { // Cursor attribute with non zero length shows cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 9); + + + { // If the cursor is hidden by the input method and the text is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 10); + + edit.setText("something"); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 11); + + { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&edit, &ev); + } + QCOMPARE(edit.isCursorVisible(), false); + QCOMPARE(spy.count(), 12); + + edit.setCursorPosition(5); + QCOMPARE(edit.isCursorVisible(), true); + QCOMPARE(spy.count(), 13); } void tst_qquicktextedit::delegateLoading_data() @@ -2568,7 +2610,7 @@ void tst_qquicktextedit::implicitSizeBinding() QFETCH(QString, wrap); QFETCH(QString, format); QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }"; - QDeclarativeComponent textComponent(&engine); + QQmlComponent textComponent(&engine); textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QScopedPointer<QObject> object(textComponent.create()); QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object.data()); @@ -2682,6 +2724,74 @@ void tst_qquicktextedit::inputMethodComposing() } QCOMPARE(edit->isInputMethodComposing(), false); QCOMPARE(spy.count(), 2); + + // Changing the text while not composing doesn't alter the composing state. + edit->setText(text.mid(0, 16)); + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); + + { + QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 3); + + // Changing the text while composing cancels composition. + edit->setText(text.mid(0, 12)); + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 4); + + { // Preedit cursor positioned outside (empty) preedit; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Cursor hidden; composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Default cursor attributes; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Selections are persisted: not composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 2, 4, QVariant())); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 6); + + edit->setCursorPosition(0); + + { // Formatting applied; composing. + QTextCharFormat format; + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 2, 4, format)); + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 7); + + { + QInputMethodEvent event; + QGuiApplication::sendEvent(edit, &event); + } + QCOMPARE(edit->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 8); } void tst_qquicktextedit::cursorRectangleSize() diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 1c8bff9d69..4f2f3cbb62 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -158,9 +158,7 @@ private slots: void focusOutClearSelection(); void echoMode(); -#ifdef QT_GUI_PASSWORD_ECHO_DELAY void passwordEchoDelay(); -#endif void geometrySignals(); void contentSize(); @@ -2368,16 +2366,16 @@ void tst_qquicktextinput::cursorDelegate() void tst_qquicktextinput::cursorVisible() { + QQuickTextInput input; + input.componentComplete(); + QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool))); + QQuickView view(testFileUrl("cursorVisible.qml")); view.show(); view.requestActivateWindow(); QTest::qWaitForWindowShown(&view); QTRY_COMPARE(&view, qGuiApp->focusWindow()); - QQuickTextInput input; - input.componentComplete(); - QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool))); - QCOMPARE(input.isCursorVisible(), false); input.setCursorVisible(true); @@ -2404,7 +2402,7 @@ void tst_qquicktextinput::cursorVisible() QCOMPARE(input.isCursorVisible(), true); QCOMPARE(spy.count(), 5); - QQuickView alternateView; + QWindow alternateView; alternateView.show(); alternateView.requestActivateWindow(); QTest::qWaitForWindowShown(&alternateView); @@ -2416,6 +2414,46 @@ void tst_qquicktextinput::cursorVisible() QTest::qWaitForWindowShown(&view); QCOMPARE(input.isCursorVisible(), true); QCOMPARE(spy.count(), 7); + + { // Cursor attribute with 0 length hides cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), false); + QCOMPARE(spy.count(), 8); + + { // Cursor attribute with non zero length shows cursor. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), true); + QCOMPARE(spy.count(), 9); + + { // If the cursor is hidden by the input method and the text is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), false); + QCOMPARE(spy.count(), 10); + + input.setText("something"); + QCOMPARE(input.isCursorVisible(), true); + QCOMPARE(spy.count(), 11); + + { // If the cursor is hidden by the input method and the cursor position is changed it should be visible again. + QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QCoreApplication::sendEvent(&input, &ev); + } + QCOMPARE(input.isCursorVisible(), false); + QCOMPARE(spy.count(), 12); + + input.setCursorPosition(5); + QCOMPARE(input.isCursorVisible(), true); + QCOMPARE(spy.count(), 13); } void tst_qquicktextinput::cursorRectangle_data() @@ -2716,9 +2754,11 @@ void tst_qquicktextinput::echoMode() QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial); } -#ifdef QT_GUI_PASSWORD_ECHO_DELAY void tst_qquicktextinput::passwordEchoDelay() { + int maskDelay = qGuiApp->styleHints()->passwordMaskDelay(); + if (maskDelay <= 0) + QSKIP("No mask delay in use"); QQuickView canvas(testFileUrl("echoMode.qml")); canvas.show(); canvas.requestActivateWindow(); @@ -2747,7 +2787,7 @@ void tst_qquicktextinput::passwordEchoDelay() QCOMPARE(input->displayText(), QString(4, fillChar)); QTest::keyPress(&canvas, '4'); QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4')); - QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY); + QTest::qWait(maskDelay); QTRY_COMPARE(input->displayText(), QString(5, fillChar)); QTest::keyPress(&canvas, '5'); QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5')); @@ -2772,7 +2812,6 @@ void tst_qquicktextinput::passwordEchoDelay() QTest::keyPress(&canvas, Qt::Key_Backspace); QCOMPARE(input->displayText(), QString(8, fillChar)); } -#endif void tst_qquicktextinput::simulateKey(QWindow *view, int key) @@ -3281,6 +3320,75 @@ void tst_qquicktextinput::inputMethodComposing() } QCOMPARE(input->isInputMethodComposing(), false); QCOMPARE(spy.count(), 2); + + // Changing the text while not composing doesn't alter the composing state. + input->setText(text.mid(0, 16)); + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 2); + + { + QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>()); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 3); + + // Changing the text while composing cancels composition. + input->setText(text.mid(0, 12)); + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 4); + + { // Preedit cursor positioned outside (empty) preedit; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + + { // Cursor hidden; composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Default cursor attributes; composing. + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 5); + + { // Selections are persisted: not composing + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, -5, 4, QVariant())); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 6); + + input->setCursorPosition(12); + + { // Formatting applied; composing. + QTextCharFormat format; + format.setUnderlineStyle(QTextCharFormat::SingleUnderline); + QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>() + << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, -5, 4, format)); + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), true); + QCOMPARE(spy.count(), 7); + + { + QInputMethodEvent event; + QGuiApplication::sendEvent(input, &event); + } + QCOMPARE(input->isInputMethodComposing(), false); + QCOMPARE(spy.count(), 8); } void tst_qquicktextinput::inputMethodUpdate() @@ -4949,7 +5057,7 @@ void tst_qquicktextinput::implicitSize() QFETCH(QString, text); QFETCH(QString, wrap); QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }"; - QDeclarativeComponent textComponent(&engine); + QQmlComponent textComponent(&engine); textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create()); @@ -4971,7 +5079,7 @@ void tst_qquicktextinput::implicitSizeBinding() QFETCH(QString, text); QFETCH(QString, wrap); QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + " }"; - QDeclarativeComponent textComponent(&engine); + QQmlComponent textComponent(&engine); textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); QScopedPointer<QObject> object(textComponent.create()); QQuickTextInput *textObject = qobject_cast<QQuickTextInput *>(object.data()); diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 73a35c1c78..78e9060acb 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -2707,9 +2707,6 @@ void tst_qquickvisualdatamodel::resolve_data() const QUrl stringListSource[] = { testFileUrl("stringlistproperties.qml"), testFileUrl("stringlistproperties-package.qml") }; - const QUrl objectListSource[] = { - testFileUrl("objectlistproperties.qml"), - testFileUrl("objectlistproperties-package.qml") }; for (int i = 0; i < 2; ++i) { // List Model. diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 654b1c86f0..b018fbf14a 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -1,12 +1,13 @@ TEMPLATE = subdirs PUBLICTESTS += \ - examples \ geometry \ nodes \ rendernode \ qquickpixmapcache +!cross_compile: PUBLICTESTS += examples + # This test requires the qtconcurrent module !contains(QT_CONFIG, concurrent):PUBLICTESTS -= qquickpixmapcache diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index d00a0e2a96..1c4319f82f 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -88,12 +88,8 @@ void QQuickViewTestUtil::flick(QQuickView *canvas, const QPoint &from, const QPo // send press, five equally spaced moves, and release. QTest::mousePress(canvas, Qt::LeftButton, 0, from); - for (int i = 0; i < pointCount; ++i) { - QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); - QGuiApplication::sendEvent(canvas, &mv); - QTest::qWait(duration/pointCount); - QCoreApplication::processEvents(); - } + for (int i = 0; i < pointCount; ++i) + QTest::mouseMove(canvas, from + (i+1)*diff/pointCount, duration / pointCount); QTest::mouseRelease(canvas, Qt::LeftButton, 0, to); QTest::qWait(50); diff --git a/tools/qmlprofiler/qmlprofiler.pro b/tools/qmlprofiler/qmlprofiler.pro index b90554455f..7ab61e7a96 100644 --- a/tools/qmlprofiler/qmlprofiler.pro +++ b/tools/qmlprofiler/qmlprofiler.pro @@ -2,7 +2,7 @@ TEMPLATE = app TARGET = qmlprofiler DESTDIR = $$QT.qml.bins -QT += qml qml-private network core-private +QT += qml qml-private v8-private network core-private target.path = $$[QT_INSTALL_BINS] INSTALLS += target diff --git a/tools/qmlscene/qmlscene.pro b/tools/qmlscene/qmlscene.pro index 3e63bd7659..464f9b3758 100644 --- a/tools/qmlscene/qmlscene.pro +++ b/tools/qmlscene/qmlscene.pro @@ -14,4 +14,4 @@ SOURCES += main.cpp CONFIG += console -DEFINES += QML_RUNTIME_TESTING QT_DECLARATIVE_DEBUG_NO_WARNING +DEFINES += QML_RUNTIME_TESTING QT_QML_DEBUG_NO_WARNING |