diff options
194 files changed, 8523 insertions, 631 deletions
diff --git a/.qmake.conf b/.qmake.conf index 3bc6c0a02b..0957f58c52 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += qt_example_installs CONFIG += warning_clean -MODULE_VERSION = 5.7.0 +MODULE_VERSION = 5.8.0 diff --git a/examples/qml/qml.pro b/examples/qml/qml.pro index d5f9fa8a9e..3cb601f542 100644 --- a/examples/qml/qml.pro +++ b/examples/qml/qml.pro @@ -1,9 +1,13 @@ TEMPLATE = subdirs -qtHaveModule(quick): SUBDIRS += \ - networkaccessmanagerfactory \ - qmlextensionplugins \ - xmlhttprequest +qtHaveModule(quick) { + SUBDIRS += \ + qmlextensionplugins \ + xmlhttprequest + + !no_network: SUBDIRS += \ + networkaccessmanagerfactory +} SUBDIRS += \ referenceexamples \ diff --git a/examples/quick/painteditem/textballoons/doc/images/declarative-textballoons_example.png b/examples/quick/customitems/painteditem/doc/images/declarative-textballoons_example.png Binary files differindex d572de597c..d572de597c 100644 --- a/examples/quick/painteditem/textballoons/doc/images/declarative-textballoons_example.png +++ b/examples/quick/customitems/painteditem/doc/images/declarative-textballoons_example.png diff --git a/examples/quick/painteditem/textballoons/doc/src/textballoons.qdoc b/examples/quick/customitems/painteditem/doc/src/textballoons.qdoc index da1658c7c6..3dc361e4a9 100644 --- a/examples/quick/painteditem/textballoons/doc/src/textballoons.qdoc +++ b/examples/quick/customitems/painteditem/doc/src/textballoons.qdoc @@ -24,8 +24,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - - /*! \title Scene Graph - Painted Item \brief Shows how to implement QPainter-based custom scenegraph items. diff --git a/examples/quick/demos/stocqt/content/StockChart.qml b/examples/quick/demos/stocqt/content/StockChart.qml index 147f513456..cd8b9f3db9 100644 --- a/examples/quick/demos/stocqt/content/StockChart.qml +++ b/examples/quick/demos/stocqt/content/StockChart.qml @@ -321,11 +321,27 @@ Rectangle { ctx.restore(); } - onPaint: { - if (!stockModel.ready) { - return; - } + function drawError(ctx, msg) + { + ctx.save(); + ctx.strokeStyle = "#888888"; + ctx.font = "24px Open Sans" + ctx.textAlign = "center" + ctx.shadowOffsetX = 4; + ctx.shadowOffsetY = 4; + ctx.shadowBlur = 1.5; + ctx.shadowColor = "#aaaaaa"; + ctx.beginPath(); + ctx.fillText(msg, (canvas.width - tickMargin) / 2, + (canvas.height - yGridOffset - yGridStep) / 2); + + ctx.closePath(); + ctx.stroke(); + ctx.restore(); + } + + onPaint: { numPoints = stockModel.indexOf(chart.startDate); if (chart.gridSize == 0) @@ -337,6 +353,11 @@ Rectangle { drawBackground(ctx); + if (!stockModel.ready) { + drawError(ctx, "No data available."); + return; + } + var highestPrice = 0; var highestVolume = 0; var lowestPrice = -1; diff --git a/examples/quick/demos/stocqt/content/StockInfo.qml b/examples/quick/demos/stocqt/content/StockInfo.qml index 3c5ee196f2..88f540fa09 100644 --- a/examples/quick/demos/stocqt/content/StockInfo.qml +++ b/examples/quick/demos/stocqt/content/StockInfo.qml @@ -73,7 +73,7 @@ Rectangle { font.family: Settings.fontFamily font.pointSize: 28 font.weight: Font.DemiBold - text: parseFloat(Math.round(root.stock.stockPrice * 100) / 100).toFixed(2); + text: parseFloat(root.stock.stockPrice).toFixed(2); } } @@ -99,7 +99,7 @@ Rectangle { color: root.stock.stockPriceChanged < 0 ? "#d40000" : "#328930" font.family: Settings.fontFamily font.pointSize: 18 - text: parseFloat(Math.round(root.stock.stockPriceChanged * 100) / 100).toFixed(2); + text: parseFloat(root.stock.stockPriceChanged).toFixed(2); } Text { @@ -110,9 +110,8 @@ Rectangle { font.pointSize: 18 font.weight: Font.DemiBold text: "(" + - Math.abs(Math.round( - root.stock.stockPriceChanged / - (root.stock.stockPrice - root.stock.stockPriceChanged) * 100)) / 100 + + parseFloat(root.stock.stockPriceChanged / + (root.stock.stockPrice - root.stock.stockPriceChanged) * 100.0).toFixed(2) + "%)" } } diff --git a/examples/quick/demos/stocqt/content/StockListModel.qml b/examples/quick/demos/stocqt/content/StockListModel.qml index b083b7bacc..be00e7bb1c 100644 --- a/examples/quick/demos/stocqt/content/StockListModel.qml +++ b/examples/quick/demos/stocqt/content/StockListModel.qml @@ -42,105 +42,105 @@ import QtQuick 2.0 ListModel { id: stocks - // Data from : http://en.wikipedia.org/wiki/NASDAQ-100 - ListElement {name: "Apple Inc."; stockId: "AAPL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + // Uncomment to test invalid entries + // ListElement {name: "The Qt Company"; stockId: "TQTC"; value: "999.0"; change: "0.0"; changePercentage: "0.0"} + + // Data from http://www.nasdaq.com/quotes/nasdaq-100-stocks.aspx + ListElement {name: "Activision Blizzard Inc."; stockId: "ATVI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Adobe Systems Inc."; stockId: "ADBE"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Analog Devices, Inc."; stockId: "ADI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Automatic Data Processing, Inc."; stockId: "ADP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Autodesk, Inc."; stockId: "ADSK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Akamai Technologies, Inc."; stockId: "AKAM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Altera Corp."; stockId: "ALTR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Alexion Pharmaceuticals, Inc."; stockId: "ALXN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Applied Materials, Inc."; stockId: "AMAT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Amgen Inc."; stockId: "AMGN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Akamai Technologies Inc."; stockId: "AKAM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Alexion Pharmaceuticals Inc."; stockId: "ALXN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Alphabet Inc."; stockId: "GOOG"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Alphabet Inc."; stockId: "GOOGL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Amazon.com Inc."; stockId: "AMZN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Activision Blizzard, Inc."; stockId: "ATVI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Avago Technologies Limited"; stockId: "AVGO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "American Airlines Group Inc."; stockId: "AAL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Amgen Inc."; stockId: "AMGN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Analog Devices Inc."; stockId: "ADI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Apple Inc."; stockId: "AAPL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Applied Materials Inc."; stockId: "AMAT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Autodesk Inc."; stockId: "ADSK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Automatic Data Processing Inc."; stockId: "ADP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Baidu Inc."; stockId: "BIDU"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Bed Bath & Beyond Inc."; stockId: "BBBY"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Baidu, Inc."; stockId: "BIDU"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Biogen Idec Inc."; stockId: "BIIB"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Broadcom Corp."; stockId: "BRCM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "CA Technologies"; stockId: "CA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Celgene Corporation"; stockId: "CELG"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Cerner Corporation"; stockId: "CERN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Biogen Inc."; stockId: "BIIB"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "BioMarin Pharmaceutical Inc."; stockId: "BMRN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Broadcom Limited"; stockId: "AVGO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "CA Inc."; stockId: "CA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Celgene Corp."; stockId: "CELG"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Cerner Corp."; stockId: "CERN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Charter Communications Inc."; stockId: "CHTR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Check Point Software Technologies Ltd."; stockId: "CHKP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "CH Robinson Worldwide Inc."; stockId: "CHRW"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Charter Communications, Inc."; stockId: "CHTR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Comcast Corporation"; stockId: "CMCSA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Costco Wholesale Corporation"; stockId: "COST"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Cisco Systems, Inc."; stockId: "CSCO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Catamaran Corporation"; stockId: "CTRX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Cognizant Technology Solutions Corporation"; stockId: "CTSH"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Citrix Systems, Inc."; stockId: "CTXS"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Discovery Communications, Inc."; stockId: "DISCA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Dish Network Corp."; stockId: "DISH"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Dollar Tree, Inc."; stockId: "DLTR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "DIRECTV"; stockId: "DTV"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Cisco Systems Inc."; stockId: "CSCO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Citrix Systems Inc."; stockId: "CTXS"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Cognizant Technology Solutions Corp."; stockId: "CTSH"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Comcast Corp."; stockId: "CMCSA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Costco Wholesale Corp."; stockId: "COST"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Ctrip.com International Ltd."; stockId: "CTRP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Discovery Communications Inc."; stockId: "DISCA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Discovery Communications Inc."; stockId: "DISCK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "DISH Network Corp."; stockId: "DISH"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Dollar Tree Inc."; stockId: "DLTR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "eBay Inc."; stockId: "EBAY"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Equinix, Inc."; stockId: "EQIX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Express Scripts Holding Company"; stockId: "ESRX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Expeditors International of Washington Inc."; stockId: "EXPD"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Expedia Inc."; stockId: "EXPE"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Fastenal Company"; stockId: "FAST"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Facebook, Inc."; stockId: "FB"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "F5 Networks, Inc."; stockId: "FFIV"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Fiserv, Inc."; stockId: "FISV"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Twenty-First Century Fox, Inc."; stockId: "FOXA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Gilead Sciences Inc."; stockId: "GILD"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Keurig Green Mountain, Inc."; stockId: "GMCR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Google Inc."; stockId: "GOOG"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Google Inc."; stockId: "GOOGL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Garmin Ltd."; stockId: "GRMN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Henry Schein, Inc."; stockId: "HSIC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Illumina Inc."; stockId: "ILMN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Intel Corporation"; stockId: "INTC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Electronic Arts Inc."; stockId: "EA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Endo International Plc"; stockId: "ENDP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Intel Corp."; stockId: "INTC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Intuit Inc."; stockId: "INTU"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Intuitive Surgical, Inc."; stockId: "ISRG"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "KLA-Tencor Corporation"; stockId: "KLAC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Kraft Foods Group, Inc."; stockId: "KRFT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Liberty Global plc"; stockId: "LBTYA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Liberty Interactive Corporation"; stockId: "LINTA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Linear Technology Corporation"; stockId: "LLTC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Liberty Media Corporation"; stockId: "LMCA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Marriott International, Inc."; stockId: "MAR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Mattel, Inc"; stockId: "MAT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Mondelez International, Inc."; stockId: "MDLZ"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Monster Beverage Corporation"; stockId: "MNST"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Microsoft Corporation"; stockId: "MSFT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Intuitive Surgical Inc."; stockId: "ISRG"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "JD.com Inc."; stockId: "JD"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "KLA-Tencor Corp."; stockId: "KLAC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Lam Research Corp."; stockId: "LRCX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Liberty Global Plc"; stockId: "LBTYA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Liberty Global Plc"; stockId: "LBTYK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Liberty Interactive Corp."; stockId: "LVNTA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Liberty Interactive Corp."; stockId: "QVCA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Liberty Media Corp."; stockId: "LMCA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Liberty Media Corp."; stockId: "LMCK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Linear Technology Corp."; stockId: "LLTC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Marriott International"; stockId: "MAR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Mattel Inc."; stockId: "MAT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Maxim Integrated Products Inc."; stockId: "MXIM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Micron Technology Inc."; stockId: "MU"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Maxim Integrated Products, Inc."; stockId: "MXIM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Mylan, Inc."; stockId: "MYL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Netflix, Inc."; stockId: "NFLX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "NetApp, Inc."; stockId: "NTAP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "NVIDIA Corporation"; stockId: "NVDA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "NXP Semiconductors NV"; stockId: "NXPI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Microsoft Corp."; stockId: "MSFT"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Mondelez International Inc."; stockId: "MDLZ"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Monster Beverage Corp."; stockId: "MNST"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Mylan N.V."; stockId: "MYL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "NetApp Inc."; stockId: "NTAP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Netflix Inc."; stockId: "NFLX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Norwegian Cruise Line Holdings Ltd."; stockId: "NCLH"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "NVIDIA Corp."; stockId: "NVDA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "NXP Semiconductors N.V."; stockId: "NXPI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "O'Reilly Automotive Inc."; stockId: "ORLY"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Paychex, Inc."; stockId: "PAYX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "PACCAR Inc."; stockId: "PCAR"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "The Priceline Group Inc."; stockId: "PCLN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "QUALCOMM Incorporated"; stockId: "QCOM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Regeneron Pharmaceuticals, Inc."; stockId: "REGN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Paychex Inc."; stockId: "PAYX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "PayPal Holdings Inc."; stockId: "PYPL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "QUALCOMM Inc."; stockId: "QCOM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Regeneron Pharmaceuticals Inc."; stockId: "REGN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Ross Stores Inc."; stockId: "ROST"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "SanDisk Corp."; stockId: "SNDK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "SBA Communications Corp."; stockId: "SBAC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Starbucks Corporation"; stockId: "SBUX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Sigma-Aldrich Corporation"; stockId: "SIAL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Seagate Technology PLC"; stockId: "STX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Sirius XM Holdings Inc."; stockId: "SIRI"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "SanDisk Corp."; stockId: "SNDK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Staples, Inc."; stockId: "SPLS"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Stericycle, Inc."; stockId: "SRCL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Seagate Technology Public Limited Company"; stockId: "STX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Symantec Corporation"; stockId: "SYMC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "TripAdvisor Inc."; stockId: "TRIP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Tractor Supply Company"; stockId: "TSCO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Tesla Motors, Inc."; stockId: "TSLA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Skyworks Solutions Inc."; stockId: "SWKS"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Starbucks Corp."; stockId: "SBUX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Stericycle Inc."; stockId: "SRCL"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Symantec Corp."; stockId: "SYMC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "T-Mobile US Inc."; stockId: "TMUS"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Tesla Motors Inc."; stockId: "TSLA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Texas Instruments Inc."; stockId: "TXN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Viacom, Inc."; stockId: "VIAB"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "VimpelCom Ltd."; stockId: "VIP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Vodafone Group Public Limited Company"; stockId: "VOD"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Verisk Analytics, Inc."; stockId: "VRSK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Vertex Pharmaceuticals Incorporated"; stockId: "VRTX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Western Digital Corporation"; stockId: "WDC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Whole Foods Market, Inc."; stockId: "WFM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} - ListElement {name: "Wynn Resorts Ltd."; stockId: "WYNN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "The Kraft Heinz Company"; stockId: "KHC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "The Priceline Group Inc."; stockId: "PCLN"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Tractor Supply Company"; stockId: "TSCO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "TripAdvisor Inc."; stockId: "TRIP"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Twenty-First Century Fox Inc."; stockId: "FOX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Twenty-First Century Fox Inc."; stockId: "FOXA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Ulta Salon Cosmetics & Fragrance Inc."; stockId: "ULTA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Verisk Analytics Inc."; stockId: "VRSK"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Vertex Pharmaceuticals Inc."; stockId: "VRTX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Viacom Inc."; stockId: "VIAB"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Vodafone Group Plc"; stockId: "VOD"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Walgreens Boots Alliance Inc."; stockId: "WBA"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Western Digital Corp."; stockId: "WDC"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Whole Foods Market Inc."; stockId: "WFM"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} ListElement {name: "Xilinx Inc."; stockId: "XLNX"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} + ListElement {name: "Yahoo! Inc."; stockId: "YHOO"; value: "0.0"; change: "0.0"; changePercentage: "0.0"} } diff --git a/examples/quick/demos/stocqt/content/StockListView.qml b/examples/quick/demos/stocqt/content/StockListView.qml index d1f735fde3..59f36b42cc 100644 --- a/examples/quick/demos/stocqt/content/StockListView.qml +++ b/examples/quick/demos/stocqt/content/StockListView.qml @@ -62,6 +62,7 @@ Rectangle { focus: true snapMode: ListView.SnapToItem model: StockListModel{} + currentIndex: -1 // Don't pre-select any item function requestUrl(stockId) { var endDate = new Date(""); //today @@ -95,7 +96,7 @@ Rectangle { xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.LOADING || xhr.readyState === XMLHttpRequest.DONE) { var records = xhr.responseText.split('\n'); - if (records.length > 0) { + if (records.length > 0 && xhr.status == 200) { var r = records[1].split(','); var today = parseFloat(r[4]); model.setProperty(index, "value", today.toFixed(2)); @@ -113,6 +114,9 @@ Rectangle { model.setProperty(index, "changePercentage", "+" + changePercentage.toFixed(2) + "%"); else model.setProperty(index, "changePercentage", changePercentage.toFixed(2) + "%"); + } else { + var unknown = "n/a"; + model.set(index, {"value": unknown, "change": unknown, "changePercentage": unknown}); } } } @@ -120,9 +124,10 @@ Rectangle { } onCurrentIndexChanged: { - mainRect.listViewActive = 0; - root.currentStockId = model.get(currentIndex).stockId; - root.currentStockName = model.get(currentIndex).name; + if (currentItem) { + root.currentStockId = model.get(currentIndex).stockId; + root.currentStockName = model.get(currentIndex).name; + } } delegate: Rectangle { @@ -132,7 +137,10 @@ Rectangle { MouseArea { anchors.fill: parent; onClicked: { - view.currentIndex = index; + if (view.currentIndex == index) + mainRect.currentIndex = 1; + else + view.currentIndex = index; } } @@ -243,7 +251,7 @@ Rectangle { } highlight: Rectangle { - width: parent.width + width: view.width color: "#eeeeee" } } diff --git a/examples/quick/demos/stocqt/content/StockModel.qml b/examples/quick/demos/stocqt/content/StockModel.qml index c1484d318e..035d9454d0 100644 --- a/examples/quick/demos/stocqt/content/StockModel.qml +++ b/examples/quick/demos/stocqt/content/StockModel.qml @@ -52,6 +52,9 @@ ListModel { signal dataReady function indexOf(date) { + if (model.count == 0) + return -1; + var newest = new Date(model.get(0).date); var oldest = new Date(model.get(model.count - 1).date); if (newest <= date) @@ -148,8 +151,11 @@ ListModel { model.ready = true; model.stockPrice = model.get(0).adjusted; model.stockPriceChanged = model.count > 1 ? (Math.round((model.stockPrice - model.get(1).close) * 100) / 100) : 0; - model.dataReady(); //emit signal + } else { + model.stockPrice = 0; + model.stockPriceChanged = 0; } + model.dataReady(); // emit signal - model.ready indicates whether the data is valid } } } diff --git a/examples/quick/demos/stocqt/stocqt.qml b/examples/quick/demos/stocqt/stocqt.qml index 512d6e1073..6b1da1713a 100644 --- a/examples/quick/demos/stocqt/stocqt.qml +++ b/examples/quick/demos/stocqt/stocqt.qml @@ -47,7 +47,7 @@ Rectangle { width: 1000 height: 700 - property int listViewActive: 0 + property alias currentIndex: root.currentIndex Rectangle { id: banner @@ -66,7 +66,7 @@ Rectangle { MouseArea { anchors.fill: parent - onClicked: listViewActive = 1; + onClicked: root.currentIndex = 0; } } @@ -108,11 +108,6 @@ Rectangle { focus: false orientation: ListView.Horizontal boundsBehavior: Flickable.StopAtBounds - currentIndex: listViewActive == 0 ? 1 : 0 - onCurrentIndexChanged: { - if (currentIndex == 1) - listViewActive = 0; - } StockModel { id: stock @@ -120,7 +115,7 @@ Rectangle { stockName: listView.currentStockName onStockIdChanged: stock.updateStock(); onDataReady: { - root.positionViewAtIndex(1, ListView.SnapPosition) + root.currentIndex = 1 stockView.update() } } diff --git a/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc b/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc new file mode 100644 index 0000000000..23e36c0469 --- /dev/null +++ b/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** 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. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \title Qt Quick Layouts - Basic Example + \example layouts + \brief Demonstrates how to use layout types to arrange a UI + \image qtquicklayouts-example-layouts.png + \ingroup qtquickexamples + + This example shows how to easily arrange UI components into + \l{Qt Quick Layouts}{layouts} with \l{GridLayout}, \l{RowLayout}, and + \l{ColumnLayout}. + + \include examples-run.qdocinc +*/ + + + diff --git a/examples/quick/layouts/layouts.pro b/examples/quick/layouts/layouts.pro new file mode 100644 index 0000000000..4d676ef49b --- /dev/null +++ b/examples/quick/layouts/layouts.pro @@ -0,0 +1,14 @@ +TEMPLATE = app + +QT += qml quick + +SOURCES += main.cpp + +RESOURCES += \ + layouts.qrc +EXAMPLE_FILES = \ + layouts.qml + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/layouts +INSTALLS += target + diff --git a/examples/quick/layouts/layouts.qml b/examples/quick/layouts/layouts.qml new file mode 100644 index 0000000000..48299f36aa --- /dev/null +++ b/examples/quick/layouts/layouts.qml @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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.2 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.3 + +ApplicationWindow { + visible: true + title: "Basic layouts" + property int margin: 11 + width: mainLayout.implicitWidth + 2 * margin + height: mainLayout.implicitHeight + 2 * margin + minimumWidth: mainLayout.Layout.minimumWidth + 2 * margin + minimumHeight: mainLayout.Layout.minimumHeight + 2 * margin + + ColumnLayout { + id: mainLayout + anchors.fill: parent + anchors.margins: margin + GroupBox { + id: rowBox + title: "Row layout" + Layout.fillWidth: true + + RowLayout { + id: rowLayout + anchors.fill: parent + TextField { + placeholderText: "This wants to grow horizontally" + Layout.fillWidth: true + } + Button { + text: "Button" + } + } + } + + GroupBox { + id: gridBox + title: "Grid layout" + Layout.fillWidth: true + + GridLayout { + id: gridLayout + rows: 3 + flow: GridLayout.TopToBottom + anchors.fill: parent + + Label { text: "Line 1" } + Label { text: "Line 2" } + Label { text: "Line 3" } + + TextField { } + TextField { } + TextField { } + + TextArea { + text: "This widget spans over three rows in the GridLayout.\n" + + "All items in the GridLayout are implicitly positioned from top to bottom." + Layout.rowSpan: 3 + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + TextArea { + id: t3 + text: "This fills the whole cell" + Layout.minimumHeight: 30 + Layout.fillHeight: true + Layout.fillWidth: true + } + GroupBox { + id: stackBox + title: "Stack layout" + implicitWidth: 200 + implicitHeight: 60 + Layout.fillWidth: true + Layout.fillHeight: true + StackLayout { + id: stackLayout + anchors.fill: parent + + function advance() { currentIndex = (currentIndex + 1) % count } + + Repeater { + id: stackRepeater + model: 5 + Rectangle { + color: Qt.hsla((0.5 + index)/stackRepeater.count, 0.3, 0.7, 1) + Button { anchors.centerIn: parent; text: "Page " + (index + 1); onClicked: { stackLayout.advance() } } + } + } + } + } + } +} diff --git a/examples/quick/layouts/layouts.qmlproject b/examples/quick/layouts/layouts.qmlproject new file mode 100644 index 0000000000..e5a8bf02ca --- /dev/null +++ b/examples/quick/layouts/layouts.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.1 + +Project { + mainFile: "main.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } +} diff --git a/examples/quick/layouts/layouts.qrc b/examples/quick/layouts/layouts.qrc new file mode 100644 index 0000000000..1e01c43fe1 --- /dev/null +++ b/examples/quick/layouts/layouts.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/layouts"> + <file>layouts.qml</file> +</qresource> +</RCC> diff --git a/examples/quick/layouts/main.cpp b/examples/quick/layouts/main.cpp new file mode 100644 index 0000000000..ef3e27a799 --- /dev/null +++ b/examples/quick/layouts/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char* argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:///layouts/layouts.qml"))); + + return app.exec(); +} diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro index c5ef46173c..e66e24882e 100644 --- a/examples/quick/quick.pro +++ b/examples/quick/quick.pro @@ -6,6 +6,7 @@ SUBDIRS = quick-accessibility \ canvas \ imageelements \ keyinteraction \ + layouts \ localstorage \ models \ views \ diff --git a/examples/quick/rendercontrol/window_singlethreaded.cpp b/examples/quick/rendercontrol/window_singlethreaded.cpp index e43093e241..45f2635ca4 100644 --- a/examples/quick/rendercontrol/window_singlethreaded.cpp +++ b/examples/quick/rendercontrol/window_singlethreaded.cpp @@ -265,9 +265,10 @@ void WindowSingleThreaded::startQuick(const QString &filename) void WindowSingleThreaded::exposeEvent(QExposeEvent *) { if (isExposed()) { - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); - if (!m_quickInitialized) + if (!m_quickInitialized) { + m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); + } } } diff --git a/src/imports/imports.pro b/src/imports/imports.pro index f7002f9ed5..ff7b6e75af 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -10,6 +10,7 @@ SUBDIRS += \ qtHaveModule(quick) { SUBDIRS += \ + layouts \ qtquick2 \ particles \ window \ diff --git a/src/imports/layouts/layouts.pro b/src/imports/layouts/layouts.pro new file mode 100644 index 0000000000..26574150de --- /dev/null +++ b/src/imports/layouts/layouts.pro @@ -0,0 +1,22 @@ +CXX_MODULE = qml +TARGET = qquicklayoutsplugin +TARGETPATH = QtQuick/Layouts +IMPORT_VERSION = 1.2 + +QT *= qml-private quick-private gui-private core-private + +SOURCES += plugin.cpp \ + qquicklayout.cpp \ + qquicklinearlayout.cpp \ + qquickstacklayout.cpp \ + qquickgridlayoutengine.cpp \ + qquicklayoutstyleinfo.cpp + +HEADERS += \ + qquicklayout_p.h \ + qquicklinearlayout_p.h \ + qquickstacklayout_p.h \ + qquickgridlayoutengine_p.h \ + qquicklayoutstyleinfo_p.h + +load(qml_plugin) diff --git a/src/imports/layouts/plugin.cpp b/src/imports/layouts/plugin.cpp new file mode 100644 index 0000000000..4552b7219b --- /dev/null +++ b/src/imports/layouts/plugin.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqmlextensionplugin.h> + +#include "qquicklinearlayout_p.h" +#include "qquickstacklayout_p.h" + +QT_BEGIN_NAMESPACE + +//![class decl] +class QtQuickLayoutsPlugin : 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.Layouts")); + Q_UNUSED(uri); + + qmlRegisterType<QQuickRowLayout>(uri, 1, 0, "RowLayout"); + qmlRegisterType<QQuickColumnLayout>(uri, 1, 0, "ColumnLayout"); + qmlRegisterType<QQuickGridLayout>(uri, 1, 0, "GridLayout"); + qmlRegisterType<QQuickStackLayout>(uri, 1, 3, "StackLayout"); + qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 0, "Layout", + QStringLiteral("Do not create objects of type Layout")); + qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 2, "Layout", + QStringLiteral("Do not create objects of type Layout")); + qmlRegisterRevision<QQuickGridLayoutBase, 1>(uri, 1, 1); + } +}; +//![class decl] + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/layouts/plugins.qmltypes b/src/imports/layouts/plugins.qmltypes new file mode 100644 index 0000000000..b130215b62 --- /dev/null +++ b/src/imports/layouts/plugins.qmltypes @@ -0,0 +1,102 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.2' + +Module { + dependencies: [] + Component { + name: "QQuickColumnLayout" + defaultProperty: "data" + prototype: "QQuickLinearLayout" + exports: ["QtQuick.Layouts/ColumnLayout 1.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QQuickGridLayout" + defaultProperty: "data" + prototype: "QQuickGridLayoutBase" + exports: ["QtQuick.Layouts/GridLayout 1.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "Flow" + values: { + "LeftToRight": 0, + "TopToBottom": 1 + } + } + Property { name: "columnSpacing"; type: "double" } + Property { name: "rowSpacing"; type: "double" } + Property { name: "columns"; type: "int" } + Property { name: "rows"; type: "int" } + Property { name: "flow"; type: "Flow" } + } + Component { + name: "QQuickGridLayoutBase" + defaultProperty: "data" + prototype: "QQuickLayout" + Property { name: "layoutDirection"; revision: 1; type: "Qt::LayoutDirection" } + Signal { name: "layoutDirectionChanged"; revision: 1 } + } + Component { + name: "QQuickLayout" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["QtQuick.Layouts/Layout 1.0", "QtQuick.Layouts/Layout 1.2"] + isCreatable: false + exportMetaObjectRevisions: [0, 0] + attachedType: "QQuickLayoutAttached" + } + Component { + name: "QQuickLayoutAttached" + prototype: "QObject" + Property { name: "minimumWidth"; type: "double" } + Property { name: "minimumHeight"; type: "double" } + Property { name: "preferredWidth"; type: "double" } + Property { name: "preferredHeight"; type: "double" } + Property { name: "maximumWidth"; type: "double" } + Property { name: "maximumHeight"; type: "double" } + Property { name: "fillHeight"; type: "bool" } + Property { name: "fillWidth"; type: "bool" } + Property { name: "row"; type: "int" } + Property { name: "column"; type: "int" } + Property { name: "rowSpan"; type: "int" } + Property { name: "columnSpan"; type: "int" } + Property { name: "alignment"; type: "Qt::Alignment" } + Property { name: "margins"; type: "double" } + Property { name: "leftMargin"; type: "double" } + Property { name: "topMargin"; type: "double" } + Property { name: "rightMargin"; type: "double" } + Property { name: "bottomMargin"; type: "double" } + } + Component { + name: "QQuickLinearLayout" + defaultProperty: "data" + prototype: "QQuickGridLayoutBase" + Property { name: "spacing"; type: "double" } + } + Component { + name: "QQuickRowLayout" + defaultProperty: "data" + prototype: "QQuickLinearLayout" + exports: ["QtQuick.Layouts/RowLayout 1.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QQuickStackLayout" + defaultProperty: "data" + prototype: "QQuickLayout" + exports: ["QtQuick.Layouts/StackLayout 1.3"] + exportMetaObjectRevisions: [0] + Property { name: "count"; type: "int"; isReadonly: true } + Property { name: "currentIndex"; type: "int" } + Method { + name: "itemAt" + type: "QQuickItem*" + Parameter { name: "index"; type: "int" } + } + } +} diff --git a/src/imports/layouts/qmldir b/src/imports/layouts/qmldir new file mode 100644 index 0000000000..00f85f7d64 --- /dev/null +++ b/src/imports/layouts/qmldir @@ -0,0 +1,5 @@ +module QtQuick.Layouts +plugin qquicklayoutsplugin +classname QtQuickLayoutsPlugin +typeinfo plugins.qmltypes +designersupported diff --git a/src/imports/layouts/qquickgridlayoutengine.cpp b/src/imports/layouts/qquickgridlayoutengine.cpp new file mode 100644 index 0000000000..fe716f0694 --- /dev/null +++ b/src/imports/layouts/qquickgridlayoutengine.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitem.h" +#include "qquickgridlayoutengine_p.h" +#include "qquicklayout_p.h" + +QT_BEGIN_NAMESPACE + +void QQuickGridLayoutEngine::setAlignment(QQuickItem *quickItem, Qt::Alignment alignment) +{ + if (QQuickGridLayoutItem *item = findLayoutItem(quickItem)) { + item->setAlignment(alignment); + invalidate(); + } +} + +Qt::Alignment QQuickGridLayoutEngine::alignment(QQuickItem *quickItem) const +{ + if (QGridLayoutItem *item = findLayoutItem(quickItem)) + return item->alignment(); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/imports/layouts/qquickgridlayoutengine_p.h b/src/imports/layouts/qquickgridlayoutengine_p.h new file mode 100644 index 0000000000..86f56a5af4 --- /dev/null +++ b/src/imports/layouts/qquickgridlayoutengine_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGRIDLAYOUTENGINE_P_H +#define QQUICKGRIDLAYOUTENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the graphics view layout classes. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGui/private/qgridlayoutengine_p.h> +#include <QtGui/private/qlayoutpolicy_p.h> +#include <QtCore/qmath.h> + +#include "qquickitem.h" +#include "qquicklayout_p.h" +#include "qdebug.h" +QT_BEGIN_NAMESPACE + +class QQuickGridLayoutItem : public QGridLayoutItem { +public: + QQuickGridLayoutItem(QQuickItem *item, int row, int column, + int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0) + : QGridLayoutItem(row, column, rowSpan, columnSpan, alignment), m_item(item), sizeHintCacheDirty(true), useFallbackToWidthOrHeight(true) {} + + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const + { + Q_UNUSED(constraint); // Quick Layouts does not support constraint atm + return effectiveSizeHints()[which]; + } + + QSizeF *effectiveSizeHints() const + { + if (!sizeHintCacheDirty) + return cachedSizeHints; + + QQuickLayout::effectiveSizeHints_helper(m_item, cachedSizeHints, 0, useFallbackToWidthOrHeight); + useFallbackToWidthOrHeight = false; + + sizeHintCacheDirty = false; + return cachedSizeHints; + } + + void setCachedSizeHints(QSizeF *sizeHints) + { + for (int i = 0; i < Qt::NSizeHints; ++i) { + cachedSizeHints[i] = sizeHints[i]; + } + sizeHintCacheDirty = false; + } + + void invalidate() + { + quickLayoutDebug() << "engine::invalidate()"; + sizeHintCacheDirty = true; + } + + QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const + { + return QQuickLayout::effectiveSizePolicy_helper(m_item, orientation, attachedLayoutObject(m_item, false)); + } + + void setGeometry(const QRectF &rect) + { + QQuickLayoutAttached *info = attachedLayoutObject(m_item, false); + const QRectF r = info ? rect.marginsRemoved(info->qMargins()) : rect; + const QSizeF oldSize(m_item->width(), m_item->height()); + const QSizeF newSize = r.size(); + m_item->setPosition(r.topLeft()); + if (newSize == oldSize) { + if (QQuickLayout *lay = qobject_cast<QQuickLayout *>(m_item)) { + if (lay->arrangementIsDirty()) + lay->rearrange(newSize); + } + } else { + m_item->setSize(newSize); + } + } + + QQuickItem *layoutItem() const { return m_item; } + + QQuickItem *m_item; +private: + mutable QSizeF cachedSizeHints[Qt::NSizeHints]; + mutable unsigned sizeHintCacheDirty : 1; + mutable unsigned useFallbackToWidthOrHeight : 1; +}; + +class QQuickGridLayoutEngine : public QGridLayoutEngine { +public: + QQuickGridLayoutEngine() : QGridLayoutEngine(Qt::AlignVCenter, true /*snapToPixelGrid*/) { } + + int indexOf(QQuickItem *item) const { + for (int i = 0; i < q_items.size(); ++i) { + if (item == static_cast<QQuickGridLayoutItem*>(q_items.at(i))->layoutItem()) + return i; + } + return -1; + } + + QQuickGridLayoutItem *findLayoutItem(QQuickItem *layoutItem) const + { + for (int i = q_items.count() - 1; i >= 0; --i) { + QQuickGridLayoutItem *item = static_cast<QQuickGridLayoutItem*>(q_items.at(i)); + if (item->layoutItem() == layoutItem) + return item; + } + return 0; + } + + void setAlignment(QQuickItem *quickItem, Qt::Alignment alignment); + Qt::Alignment alignment(QQuickItem *quickItem) const; + +}; + + + +QT_END_NAMESPACE + +#endif // QQUICKGRIDLAYOUTENGINE_P_H diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp new file mode 100644 index 0000000000..d4d4e1703d --- /dev/null +++ b/src/imports/layouts/qquicklayout.cpp @@ -0,0 +1,1081 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklayout_p.h" +#include <QEvent> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qmath.h> +#include <limits> + +/*! + \qmltype Layout + \instantiates QQuickLayoutAttached + \inqmlmodule QtQuick.Layouts + \ingroup layouts + \brief Provides attached properties for items pushed onto a \l GridLayout, + \l RowLayout or \l ColumnLayout. + + An object of type Layout is attached to children of the layout to provide layout specific + information about the item. + The properties of the attached object influence how the layout will arrange the items. + + For instance, you can specify \l minimumWidth, \l preferredWidth, and + \l maximumWidth if the default values are not satisfactory. + + When a layout is resized, items may grow or shrink. Due to this, items have a + \l{Layout::minimumWidth}{minimum size}, \l{Layout::preferredWidth}{preferred size} and a + \l{Layout::maximumWidth}{maximum size}. + + If minimum size has not been explicitly specified on an item, the size is set to \c 0. + If maximum size has not been explicitly specified on an item, the size is set to + \c Number.POSITIVE_INFINITY. + + For layouts, the implicit minimum and maximum sizes depend on the content of the layouts. + + The \l fillWidth and \l fillHeight properties can either be \c true or \c false. If they are \c + false, the item's size will be fixed to its preferred size. Otherwise, it will grow or shrink + between its minimum and maximum size as the layout is resized. + + \note It is not recommended to have bindings to the x, y, width, or height properties of items + in a layout, since this would conflict with the goals of Layout, and can also cause binding + loops. + + + \sa GridLayout + \sa RowLayout + \sa ColumnLayout +*/ + +QT_BEGIN_NAMESPACE + +QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent) + : QObject(parent), + m_minimumWidth(0), + m_minimumHeight(0), + m_preferredWidth(-1), + m_preferredHeight(-1), + m_maximumWidth(std::numeric_limits<qreal>::infinity()), + m_maximumHeight(std::numeric_limits<qreal>::infinity()), + m_defaultMargins(0), + m_row(-1), + m_column(-1), + m_rowSpan(1), + m_columnSpan(1), + m_fillWidth(false), + m_fillHeight(false), + m_isFillWidthSet(false), + m_isFillHeightSet(false), + m_isMinimumWidthSet(false), + m_isMinimumHeightSet(false), + m_isMaximumWidthSet(false), + m_isMaximumHeightSet(false), + m_changesNotificationEnabled(true), + m_isLeftMarginSet(false), + m_isTopMarginSet(false), + m_isRightMarginSet(false), + m_isBottomMarginSet(false), + m_alignment(0) +{ + +} + +/*! + \qmlattachedproperty real Layout::minimumWidth + + This property holds the minimum width of an item in a layout. + The default value is the item's implicit minimum width. + + If the item is a layout, the implicit minimum width will be the minimum width the layout can + have without any of its items shrinking below their minimum width. + The implicit minimum width for any other item is \c 0. + + Setting this value to -1 will reset the width back to its implicit minimum width. + + + \sa preferredWidth + \sa maximumWidth +*/ +void QQuickLayoutAttached::setMinimumWidth(qreal width) +{ + if (qIsNaN(width)) + return; + m_isMinimumWidthSet = width >= 0; + if (m_minimumWidth == width) + return; + + m_minimumWidth = width; + invalidateItem(); + emit minimumWidthChanged(); +} + +/*! + \qmlattachedproperty real Layout::minimumHeight + + This property holds the minimum height of an item in a layout. + The default value is the item's implicit minimum height. + + If the item is a layout, the implicit minimum height will be the minimum height the layout can + have without any of its items shrinking below their minimum height. + The implicit minimum height for any other item is \c 0. + + Setting this value to -1 will reset the height back to its implicit minimum height. + + \sa preferredHeight + \sa maximumHeight +*/ +void QQuickLayoutAttached::setMinimumHeight(qreal height) +{ + if (qIsNaN(height)) + return; + m_isMinimumHeightSet = height >= 0; + if (m_minimumHeight == height) + return; + + m_minimumHeight = height; + invalidateItem(); + emit minimumHeightChanged(); +} + +/*! + \qmlattachedproperty real Layout::preferredWidth + + This property holds the preferred width of an item in a layout. + If the preferred width is \c -1 it will be ignored, and the layout + will use \l{Item::implicitWidth}{implicitWidth} instead. + The default is \c -1. + + \sa minimumWidth + \sa maximumWidth +*/ +void QQuickLayoutAttached::setPreferredWidth(qreal width) +{ + if (qIsNaN(width) || m_preferredWidth == width) + return; + + m_preferredWidth = width; + invalidateItem(); + emit preferredWidthChanged(); +} + +/*! + \qmlattachedproperty real Layout::preferredHeight + + This property holds the preferred height of an item in a layout. + If the preferred height is \c -1 it will be ignored, and the layout + will use \l{Item::implicitHeight}{implicitHeight} instead. + The default is \c -1. + + \sa minimumHeight + \sa maximumHeight +*/ +void QQuickLayoutAttached::setPreferredHeight(qreal height) +{ + if (qIsNaN(height) || m_preferredHeight == height) + return; + + m_preferredHeight = height; + invalidateItem(); + emit preferredHeightChanged(); +} + +/*! + \qmlattachedproperty real Layout::maximumWidth + + This property holds the maximum width of an item in a layout. + The default value is the item's implicit maximum width. + + If the item is a layout, the implicit maximum width will be the maximum width the layout can + have without any of its items growing beyond their maximum width. + The implicit maximum width for any other item is \c Number.POSITIVE_INFINITY. + + Setting this value to \c -1 will reset the width back to its implicit maximum width. + + \sa minimumWidth + \sa preferredWidth +*/ +void QQuickLayoutAttached::setMaximumWidth(qreal width) +{ + if (qIsNaN(width)) + return; + m_isMaximumWidthSet = width >= 0; + if (m_maximumWidth == width) + return; + + m_maximumWidth = width; + invalidateItem(); + emit maximumWidthChanged(); +} + +/*! + \qmlattachedproperty real Layout::maximumHeight + + The default value is the item's implicit maximum height. + + If the item is a layout, the implicit maximum height will be the maximum height the layout can + have without any of its items growing beyond their maximum height. + The implicit maximum height for any other item is \c Number.POSITIVE_INFINITY. + + Setting this value to \c -1 will reset the height back to its implicit maximum height. + + \sa minimumHeight + \sa preferredHeight +*/ +void QQuickLayoutAttached::setMaximumHeight(qreal height) +{ + if (qIsNaN(height)) + return; + m_isMaximumHeightSet = height >= 0; + if (m_maximumHeight == height) + return; + + m_maximumHeight = height; + invalidateItem(); + emit maximumHeightChanged(); +} + +void QQuickLayoutAttached::setMinimumImplicitSize(const QSizeF &sz) +{ + bool emitWidthChanged = false; + bool emitHeightChanged = false; + if (!m_isMinimumWidthSet && m_minimumWidth != sz.width()) { + m_minimumWidth = sz.width(); + emitWidthChanged = true; + } + if (!m_isMinimumHeightSet && m_minimumHeight != sz.height()) { + m_minimumHeight = sz.height(); + emitHeightChanged = true; + } + // Only invalidate the item once, and make sure we emit signal changed after the call to + // invalidateItem() + if (emitWidthChanged || emitHeightChanged) { + invalidateItem(); + if (emitWidthChanged) + emit minimumWidthChanged(); + if (emitHeightChanged) + emit minimumHeightChanged(); + } +} + +void QQuickLayoutAttached::setMaximumImplicitSize(const QSizeF &sz) +{ + bool emitWidthChanged = false; + bool emitHeightChanged = false; + if (!m_isMaximumWidthSet && m_maximumWidth != sz.width()) { + m_maximumWidth = sz.width(); + emitWidthChanged = true; + } + if (!m_isMaximumHeightSet && m_maximumHeight != sz.height()) { + m_maximumHeight = sz.height(); + emitHeightChanged = true; + } + // Only invalidate the item once, and make sure we emit changed signal after the call to + // invalidateItem() + if (emitWidthChanged || emitHeightChanged) { + invalidateItem(); + if (emitWidthChanged) + emit maximumWidthChanged(); + if (emitHeightChanged) + emit maximumHeightChanged(); + } +} + +/*! + \qmlattachedproperty bool Layout::fillWidth + + If this property is \c true, the item will be as wide as possible while respecting + the given constraints. If the property is \c false, the item will have a fixed width + set to the preferred width. + The default is \c false, except for layouts themselves, which default to \c true. + + \sa fillHeight +*/ +void QQuickLayoutAttached::setFillWidth(bool fill) +{ + m_isFillWidthSet = true; + if (m_fillWidth != fill) { + m_fillWidth = fill; + invalidateItem(); + emit fillWidthChanged(); + } +} + +/*! + \qmlattachedproperty bool Layout::fillHeight + + If this property is \c true, the item will be as tall as possible while respecting + the given constraints. If the property is \c false, the item will have a fixed height + set to the preferred height. + The default is \c false, except for layouts themselves, which default to \c true. + + \sa fillWidth +*/ +void QQuickLayoutAttached::setFillHeight(bool fill) +{ + m_isFillHeightSet = true; + if (m_fillHeight != fill) { + m_fillHeight = fill; + invalidateItem(); + emit fillHeightChanged(); + } +} + +/*! + \qmlattachedproperty int Layout::row + + This property allows you to specify the row position of an item in a \l GridLayout. + + If both \l column and this property are not set, it is up to the layout to assign a cell to the item. + + The default value is \c 0. + + \sa column + \sa rowSpan +*/ +void QQuickLayoutAttached::setRow(int row) +{ + if (row >= 0 && row != m_row) { + m_row = row; + repopulateLayout(); + emit rowChanged(); + } +} + +/*! + \qmlattachedproperty int Layout::column + + This property allows you to specify the column position of an item in a \l GridLayout. + + If both \l row and this property are not set, it is up to the layout to assign a cell to the item. + + The default value is \c 0. + + \sa row + \sa columnSpan +*/ +void QQuickLayoutAttached::setColumn(int column) +{ + if (column >= 0 && column != m_column) { + m_column = column; + repopulateLayout(); + emit columnChanged(); + } +} + + +/*! + \qmlattachedproperty Qt.Alignment Layout::alignment + + This property allows you to specify the alignment of an item within the cell(s) it occupies. + + The default value is \c 0, which means it will be \c{Qt.AlignVCenter | Qt.AlignLeft} + + A valid alignment is a combination of the following flags: + \list + \li Qt::AlignLeft + \li Qt::AlignHCenter + \li Qt::AlignRight + \li Qt::AlignTop + \li Qt::AlignVCenter + \li Qt::AlignBottom + \li Qt::AlignBaseline + \endlist + +*/ +void QQuickLayoutAttached::setAlignment(Qt::Alignment align) +{ + if (align != m_alignment) { + m_alignment = align; + if (QQuickLayout *layout = parentLayout()) { + layout->setAlignment(item(), align); + invalidateItem(); + } + emit alignmentChanged(); + } +} + +/*! + \qmlattachedproperty real Layout::margins + + Sets the margins outside of an item to all have the same value. The item + itself does not evaluate its own margins. It is the parent's responsibility + to decide if it wants to evaluate the margins. + + Specifically, margins are only evaluated by ColumnLayout, RowLayout, + GridLayout, and other layout-like containers, such as SplitView, where the + effective cell size of an item will be increased as the margins are + increased. + + Therefore, if an item with margins is a child of another \c Item, its + position, size and implicit size will remain unchanged. + + Combining margins with alignment will align the item \e including its + margins. For instance, a vertically-centered Item with a top margin of \c 1 + and a bottom margin of \c 9 will cause the Items effective alignment within + the cell to be 4 pixels above the center. + + The default value is \c 0. + + \sa leftMargin + \sa topMargin + \sa rightMargin + \sa bottomMargin + + \since QtQuick.Layouts 1.2 +*/ +void QQuickLayoutAttached::setMargins(qreal m) +{ + if (m == m_defaultMargins) + return; + + m_defaultMargins = m; + invalidateItem(); + if (!m_isLeftMarginSet && m_margins.left() != m) + emit leftMarginChanged(); + if (!m_isTopMarginSet && m_margins.top() != m) + emit topMarginChanged(); + if (!m_isRightMarginSet && m_margins.right() != m) + emit rightMarginChanged(); + if (!m_isBottomMarginSet && m_margins.bottom() != m) + emit bottomMarginChanged(); + emit marginsChanged(); +} + +/*! + \qmlattachedproperty real Layout::leftMargin + + Specifies the left margin outside of an item. + If the value is not set, it will use the value from \l margins. + + \sa margins + + \since QtQuick.Layouts 1.2 +*/ +void QQuickLayoutAttached::setLeftMargin(qreal m) +{ + const bool changed = leftMargin() != m; + m_margins.setLeft(m); + m_isLeftMarginSet = true; + if (changed) { + invalidateItem(); + emit leftMarginChanged(); + } +} + +void QQuickLayoutAttached::resetLeftMargin() +{ + const bool changed = m_isLeftMarginSet && (m_defaultMargins != m_margins.left()); + m_isLeftMarginSet = false; + if (changed) { + invalidateItem(); + emit leftMarginChanged(); + } +} + +/*! + \qmlattachedproperty real Layout::topMargin + + Specifies the top margin outside of an item. + If the value is not set, it will use the value from \l margins. + + \sa margins + + \since QtQuick.Layouts 1.2 +*/ +void QQuickLayoutAttached::setTopMargin(qreal m) +{ + const bool changed = topMargin() != m; + m_margins.setTop(m); + m_isTopMarginSet = true; + if (changed) { + invalidateItem(); + emit topMarginChanged(); + } +} + +void QQuickLayoutAttached::resetTopMargin() +{ + const bool changed = m_isTopMarginSet && (m_defaultMargins != m_margins.top()); + m_isTopMarginSet = false; + if (changed) { + invalidateItem(); + emit topMarginChanged(); + } +} + +/*! + \qmlattachedproperty real Layout::rightMargin + + Specifies the right margin outside of an item. + If the value is not set, it will use the value from \l margins. + + \sa margins + + \since QtQuick.Layouts 1.2 +*/ +void QQuickLayoutAttached::setRightMargin(qreal m) +{ + const bool changed = rightMargin() != m; + m_margins.setRight(m); + m_isRightMarginSet = true; + if (changed) { + invalidateItem(); + emit rightMarginChanged(); + } +} + +void QQuickLayoutAttached::resetRightMargin() +{ + const bool changed = m_isRightMarginSet && (m_defaultMargins != m_margins.right()); + m_isRightMarginSet = false; + if (changed) { + invalidateItem(); + emit rightMarginChanged(); + } +} + +/*! + \qmlattachedproperty real Layout::bottomMargin + + Specifies the bottom margin outside of an item. + If the value is not set, it will use the value from \l margins. + + \sa margins + + \since QtQuick.Layouts 1.2 +*/ +void QQuickLayoutAttached::setBottomMargin(qreal m) +{ + const bool changed = bottomMargin() != m; + m_margins.setBottom(m); + m_isBottomMarginSet = true; + if (changed) { + invalidateItem(); + emit bottomMarginChanged(); + } +} + +void QQuickLayoutAttached::resetBottomMargin() +{ + const bool changed = m_isBottomMarginSet && (m_defaultMargins != m_margins.bottom()); + m_isBottomMarginSet = false; + if (changed) { + invalidateItem(); + emit bottomMarginChanged(); + } +} + + +/*! + \qmlattachedproperty int Layout::rowSpan + + This property allows you to specify the row span of an item in a \l GridLayout. + + The default value is \c 1. + + \sa columnSpan + \sa row +*/ +void QQuickLayoutAttached::setRowSpan(int span) +{ + if (span != m_rowSpan) { + m_rowSpan = span; + repopulateLayout(); + emit rowSpanChanged(); + } +} + + +/*! + \qmlattachedproperty int Layout::columnSpan + + This property allows you to specify the column span of an item in a \l GridLayout. + + The default value is \c 1. + + \sa rowSpan + \sa column +*/ +void QQuickLayoutAttached::setColumnSpan(int span) +{ + if (span != m_columnSpan) { + m_columnSpan = span; + repopulateLayout(); + emit columnSpanChanged(); + } +} + + +qreal QQuickLayoutAttached::sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const +{ + qreal result = 0; + if (QQuickLayout *layout = qobject_cast<QQuickLayout *>(item())) { + const QSizeF sz = layout->sizeHint(which); + result = (orientation == Qt::Horizontal ? sz.width() : sz.height()); + } else { + if (which == Qt::MaximumSize) + result = std::numeric_limits<qreal>::infinity(); + } + return result; +} + +void QQuickLayoutAttached::invalidateItem() +{ + if (!m_changesNotificationEnabled) + return; + quickLayoutDebug() << "QQuickLayoutAttached::invalidateItem"; + if (QQuickLayout *layout = parentLayout()) { + layout->invalidate(item()); + } +} + +void QQuickLayoutAttached::repopulateLayout() +{ + if (QQuickLayout *layout = parentLayout()) + layout->updateLayoutItems(); +} + +QQuickLayout *QQuickLayoutAttached::parentLayout() const +{ + QQuickItem *parentItem = item(); + if (parentItem) { + parentItem = parentItem->parentItem(); + return qobject_cast<QQuickLayout *>(parentItem); + } else { + qWarning("Layout must be attached to Item elements"); + } + return 0; +} + +QQuickItem *QQuickLayoutAttached::item() const +{ + return qobject_cast<QQuickItem *>(parent()); +} + + +QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent) + : QQuickItem(dd, parent), + m_dirty(false) +{ +} + +QQuickLayout::~QQuickLayout() +{ + d_func()->m_isReady = false; +} + +QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object) +{ + return new QQuickLayoutAttached(object); +} + +void QQuickLayout::updatePolish() +{ + rearrange(QSizeF(width(), height())); +} + +void QQuickLayout::componentComplete() +{ + Q_D(QQuickLayout); + d->m_disableRearrange = true; + QQuickItem::componentComplete(); // will call our geometryChanged(), (where isComponentComplete() == true) + d->m_disableRearrange = false; + d->m_isReady = true; +} + +void QQuickLayout::invalidate(QQuickItem * /*childItem*/) +{ + if (m_dirty) + return; + + m_dirty = true; + + if (!qobject_cast<QQuickLayout *>(parentItem())) { + quickLayoutDebug() << "QQuickLayout::invalidate(), polish()"; + polish(); + } +} + +bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const +{ + Q_D(const QQuickLayout); + bool ignoreItem = true; + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); + if (childPrivate->explicitVisible) { + effectiveSizeHints_helper(child, sizeHints, &info, true); + QSizeF effectiveMaxSize = sizeHints[Qt::MaximumSize]; + if (!effectiveMaxSize.isNull()) { + QSizeF &prefS = sizeHints[Qt::PreferredSize]; + if (effectiveSizePolicy_helper(child, Qt::Horizontal, info) == QLayoutPolicy::Fixed) + effectiveMaxSize.setWidth(prefS.width()); + if (effectiveSizePolicy_helper(child, Qt::Vertical, info) == QLayoutPolicy::Fixed) + effectiveMaxSize.setHeight(prefS.height()); + } + ignoreItem = effectiveMaxSize.isNull(); + } + + if (ignoreItem) + d->m_ignoredItems << child; + return ignoreItem; +} +struct QQuickItemPublic : public QQuickItem { + static bool isCompleted(QQuickItem *item) { + return static_cast<QQuickItemPublic*>(item)->isComponentComplete(); + } +}; + +void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == ItemChildAddedChange) { + QQuickItem *item = value.item; + QObject::connect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem())); + QObject::connect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem())); + QObject::connect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem())); + QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder); + if (isReady()) + updateLayoutItems(); + } else if (change == ItemChildRemovedChange) { + QQuickItem *item = value.item; + QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem())); + QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem())); + QObject::disconnect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem())); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder); + if (isReady()) + updateLayoutItems(); + } + QQuickItem::itemChange(change, value); +} + +void QQuickLayout::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickLayout); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + if (d->m_disableRearrange || !isReady() || !newGeometry.isValid()) + return; + + quickLayoutDebug() << "QQuickStackLayout::geometryChanged" << newGeometry << oldGeometry; + rearrange(newGeometry.size()); +} + +void QQuickLayout::invalidateSenderItem() +{ + if (!isReady()) + return; + QQuickItem *item = static_cast<QQuickItem *>(sender()); + Q_ASSERT(item); + invalidate(item); +} + +bool QQuickLayout::isReady() const +{ + return d_func()->m_isReady; +} + +void QQuickLayout::itemSiblingOrderChanged(QQuickItem *item) +{ + Q_UNUSED(item); + updateLayoutItems(); +} + +void QQuickLayout::rearrange(const QSizeF &/*size*/) +{ + m_dirty = false; +} + + +/* + The layout engine assumes: + 1. minimum <= preferred <= maximum + 2. descent is within minimum and maximum bounds (### verify) + + This function helps to ensure that by the following rules (in the following order): + 1. If minimum > maximum, set minimum = maximum + 2. Clamp preferred to be between the [minimum,maximum] range. + 3. If descent > minimum, set descent = minimum (### verify if this is correct, it might + need some refinements to multiline texts) + + If any values are "not set" (i.e. negative), they will be left untouched, so that we + know which values needs to be fetched from the implicit hints (not user hints). + */ +static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent) +{ + if (minimum >= 0 && maximum >= 0 && minimum > maximum) + minimum = maximum; + + if (preferred >= 0) { + if (minimum >= 0 && preferred < minimum) { + preferred = minimum; + } else if (maximum >= 0 && preferred > maximum) { + preferred = maximum; + } + } + + if (minimum >= 0 && descent > minimum) + descent = minimum; +} + +static void boundSize(QSizeF &result, const QSizeF &size) +{ + if (size.width() >= 0 && size.width() < result.width()) + result.setWidth(size.width()); + if (size.height() >= 0 && size.height() < result.height()) + result.setHeight(size.height()); +} + +static void expandSize(QSizeF &result, const QSizeF &size) +{ + if (size.width() >= 0 && size.width() > result.width()) + result.setWidth(size.width()); + if (size.height() >= 0 && size.height() > result.height()) + result.setHeight(size.height()); +} + +static inline void combineHints(qreal ¤t, qreal fallbackHint) +{ + if (current < 0) + current = fallbackHint; +} + +static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize) +{ + combineHints(result.rwidth(), fallbackSize.width()); + combineHints(result.rheight(), fallbackSize.height()); +} + +static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size) +{ + if (!info) return; + + Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize); + + const QSizeF constraint(which == Qt::MinimumSize + ? QSizeF(info->minimumWidth(), info->minimumHeight()) + : QSizeF(info->maximumWidth(), info->maximumHeight())); + + if (!info->isExtentExplicitlySet(Qt::Horizontal, which)) + combineHints(size->rwidth(), constraint.width()); + if (!info->isExtentExplicitlySet(Qt::Vertical, which)) + combineHints(size->rheight(), constraint.height()); +} + +typedef qreal (QQuickLayoutAttached::*SizeGetter)() const; + +/*! + \internal + Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo. + + It is like this is because it enables it to be reused. + + The goal of this function is to return the effective minimum, preferred and maximum size hints + that the layout will use for this item. + This function takes care of gathering all explicitly set size hints, normalizes them so + that min < pref < max. + Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints, + which is usually derived from the content of the layouts (or items). + + The following table illustrates the preference of the properties used for measuring layout + items. If present, the USER properties will be preferred. If USER properties are not present, + the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an + ultimate fallback. + + Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been + explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This + determines if it should be used as a USER or as a HINT value. + + Fractional size hints will be ceiled to the closest integer. This is in order to give some + slack when the items are snapped to the pixel grid. + + | *Minimum* | *Preferred* | *Maximum* | ++----------------+----------------------+-----------------------+--------------------------+ +|USER (explicit) | Layout.minimumWidth | Layout.preferredWidth | Layout.maximumWidth | +|HINT (implicit) | Layout.minimumWidth | implicitWidth | Layout.maximumWidth | +|FALLBACK | 0 | width | Number.POSITIVE_INFINITY | ++----------------+----------------------+-----------------------+--------------------------+ + */ +void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight) +{ + for (int i = 0; i < Qt::NSizeHints; ++i) + cachedSizeHints[i] = QSizeF(); + QQuickLayoutAttached *info = attachedLayoutObject(item, false); + // First, retrieve the user-specified hints from the attached "Layout." properties + if (info) { + struct Getters { + SizeGetter call[NSizes]; + }; + + static Getters horGetters = { + {&QQuickLayoutAttached::minimumWidth, &QQuickLayoutAttached::preferredWidth, &QQuickLayoutAttached::maximumWidth}, + }; + + static Getters verGetters = { + {&QQuickLayoutAttached::minimumHeight, &QQuickLayoutAttached::preferredHeight, &QQuickLayoutAttached::maximumHeight} + }; + for (int i = 0; i < NSizes; ++i) { + SizeGetter getter = horGetters.call[i]; + Q_ASSERT(getter); + + if (info->isExtentExplicitlySet(Qt::Horizontal, (Qt::SizeHint)i)) + cachedSizeHints[i].setWidth((info->*getter)()); + + getter = verGetters.call[i]; + Q_ASSERT(getter); + if (info->isExtentExplicitlySet(Qt::Vertical, (Qt::SizeHint)i)) + cachedSizeHints[i].setHeight((info->*getter)()); + } + } + + QSizeF &minS = cachedSizeHints[Qt::MinimumSize]; + QSizeF &prefS = cachedSizeHints[Qt::PreferredSize]; + QSizeF &maxS = cachedSizeHints[Qt::MaximumSize]; + QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent]; + + // For instance, will normalize the following user-set hints + // from: [10, 5, 60] + // to: [10, 10, 60] + normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth()); + normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight()); + + // All explicit values gathered, now continue to gather the implicit sizes + + //--- GATHER MAXIMUM SIZE HINTS --- + combineImplicitHints(info, Qt::MaximumSize, &maxS); + combineSize(maxS, QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity())); + // implicit max or min sizes should not limit an explicitly set preferred size + expandSize(maxS, prefS); + expandSize(maxS, minS); + + //--- GATHER MINIMUM SIZE HINTS --- + combineImplicitHints(info, Qt::MinimumSize, &minS); + expandSize(minS, QSizeF(0,0)); + boundSize(minS, prefS); + boundSize(minS, maxS); + + //--- GATHER PREFERRED SIZE HINTS --- + // First, from implicitWidth/Height + qreal &prefWidth = prefS.rwidth(); + qreal &prefHeight = prefS.rheight(); + if (prefWidth < 0 && item->implicitWidth() > 0) + prefWidth = qCeil(item->implicitWidth()); + if (prefHeight < 0 && item->implicitHeight() > 0) + prefHeight = qCeil(item->implicitHeight()); + + // If that fails, make an ultimate fallback to width/height + + if (!info && (prefWidth < 0 || prefHeight < 0)) + info = attachedLayoutObject(item); + + if (useFallbackToWidthOrHeight && info) { + /* This block is a bit hacky, but if we want to support using width/height + as preferred size hints in layouts, (which we think most people expect), + we only want to use the initial width. + This is because the width will change due to layout rearrangement, and the preferred + width should return the same value, regardless of the current width. + We therefore store the width in the implicitWidth attached property. + Since the layout listens to changes of implicitWidth, (it will + basically cause an invalidation of the layout), we have to disable that + notification while we set the implicit width (and height). + + Only use this fallback the first time the size hint is queried. Otherwise, we might + end up picking a width that is different than what was specified in the QML. + */ + if (prefWidth < 0 || prefHeight < 0) { + item->blockSignals(true); + if (prefWidth < 0) { + prefWidth = item->width(); + item->setImplicitWidth(prefWidth); + } + if (prefHeight < 0) { + prefHeight = item->height(); + item->setImplicitHeight(prefHeight); + } + item->blockSignals(false); + } + } + + + + // Normalize again after the implicit hints have been gathered + expandSize(prefS, minS); + boundSize(prefS, maxS); + + //--- GATHER DESCENT + // Minimum descent is only applicable for the effective minimum height, + // so we gather the descent last. + const qreal minimumDescent = minS.height() - item->baselineOffset(); + descentS.setHeight(minimumDescent); + + if (info) { + QMarginsF margins = info->qMargins(); + QSizeF extraMargins(margins.left() + margins.right(), margins.top() + margins.bottom()); + minS += extraMargins; + prefS += extraMargins; + maxS += extraMargins; + descentS += extraMargins; + } + if (attachedInfo) + *attachedInfo = info; +} + +/*! + \internal + + Assumes \a info is set (if the object has an attached property) + */ +QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info) +{ + bool fillExtent = false; + bool isSet = false; + if (info) { + if (orientation == Qt::Horizontal) { + isSet = info->isFillWidthSet(); + if (isSet) fillExtent = info->fillWidth(); + } else { + isSet = info->isFillHeightSet(); + if (isSet) fillExtent = info->fillHeight(); + } + } + if (!isSet && qobject_cast<QQuickLayout*>(item)) + fillExtent = true; + return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed; + +} + + + +QT_END_NAMESPACE diff --git a/src/imports/layouts/qquicklayout_p.h b/src/imports/layouts/qquicklayout_p.h new file mode 100644 index 0000000000..c7f04c1fed --- /dev/null +++ b/src/imports/layouts/qquicklayout_p.h @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLAYOUT_P_H +#define QQUICKLAYOUT_P_H + +#include <QPointer> +#include <QQuickItem> +#include <private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> +#include <QtGui/private/qlayoutpolicy_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickLayoutAttached; + +#if 0 && !defined(QT_NO_DEBUG) && !defined(QT_NO_DEBUG_OUTPUT) +# define quickLayoutDebug QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug +#else +# define quickLayoutDebug QT_NO_QDEBUG_MACRO +#endif + +class QQuickLayoutPrivate; +class QQuickLayout : public QQuickItem, public QQuickItemChangeListener + +{ + Q_OBJECT +public: + enum SizeHint { + MinimumSize = 0, + PreferredSize, + MaximumSize, + NSizes + }; + + explicit QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent = 0); + ~QQuickLayout(); + + static QQuickLayoutAttached *qmlAttachedProperties(QObject *object); + + + void componentComplete() Q_DECL_OVERRIDE; + virtual QSizeF sizeHint(Qt::SizeHint whichSizeHint) const = 0; + virtual void setAlignment(QQuickItem *item, Qt::Alignment align) = 0; + virtual void invalidate(QQuickItem * childItem = 0); + virtual void updateLayoutItems() = 0; + + // iterator + virtual QQuickItem *itemAt(int index) const = 0; + virtual int itemCount() const = 0; + + virtual void rearrange(const QSizeF &); + bool arrangementIsDirty() const { return m_dirty; } + + static void effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **info, bool useFallbackToWidthOrHeight); + static QLayoutPolicy::Policy effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info); + bool shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const; + + void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + bool isReady() const; + + + /* QQuickItemChangeListener */ + void itemSiblingOrderChanged(QQuickItem *item) Q_DECL_OVERRIDE; + +protected: + void updatePolish() Q_DECL_OVERRIDE; + + enum Orientation { + Vertical = 0, + Horizontal, + NOrientations + }; + +protected slots: + void invalidateSenderItem(); + +private: + bool m_dirty; + + Q_DECLARE_PRIVATE(QQuickLayout) + + friend class QQuickLayoutAttached; +}; + + +class QQuickLayoutPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickLayout) +public: + QQuickLayoutPrivate() : m_isReady(false), m_disableRearrange(true) {} + +protected: + unsigned m_isReady : 1; + unsigned m_disableRearrange : 1; + mutable QSet<QQuickItem *> m_ignoredItems; +}; + + +class QQuickLayoutAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) + Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged) + Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged) + Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged) + Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged) + Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged) + Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged) + Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged) + Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged) + Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged) + Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged) + Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan NOTIFY columnSpanChanged) + Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged) + + Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged) + Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged) + Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged) + Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged) + Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged) + +public: + QQuickLayoutAttached(QObject *object); + + qreal minimumWidth() const { return !m_isMinimumWidthSet ? sizeHint(Qt::MinimumSize, Qt::Horizontal) : m_minimumWidth; } + void setMinimumWidth(qreal width); + + qreal minimumHeight() const { return !m_isMinimumHeightSet ? sizeHint(Qt::MinimumSize, Qt::Vertical) : m_minimumHeight; } + void setMinimumHeight(qreal height); + + qreal preferredWidth() const { return m_preferredWidth; } + void setPreferredWidth(qreal width); + + qreal preferredHeight() const { return m_preferredHeight; } + void setPreferredHeight(qreal width); + + qreal maximumWidth() const { return !m_isMaximumWidthSet ? sizeHint(Qt::MaximumSize, Qt::Horizontal) : m_maximumWidth; } + void setMaximumWidth(qreal width); + + qreal maximumHeight() const { return !m_isMaximumHeightSet ? sizeHint(Qt::MaximumSize, Qt::Vertical) : m_maximumHeight; } + void setMaximumHeight(qreal height); + + void setMinimumImplicitSize(const QSizeF &sz); + void setMaximumImplicitSize(const QSizeF &sz); + + bool fillWidth() const { return m_fillWidth; } + void setFillWidth(bool fill); + bool isFillWidthSet() const { return m_isFillWidthSet; } + + bool fillHeight() const { return m_fillHeight; } + void setFillHeight(bool fill); + bool isFillHeightSet() const { return m_isFillHeightSet; } + + int row() const { return qMax(m_row, 0); } + void setRow(int row); + bool isRowSet() const { return m_row >= 0; } + int column() const { return qMax(m_column, 0); } + void setColumn(int column); + bool isColumnSet() const { return m_column >= 0; } + + int rowSpan() const { return m_rowSpan; } + void setRowSpan(int span); + int columnSpan() const { return m_columnSpan; } + void setColumnSpan(int span); + + Qt::Alignment alignment() const { return m_alignment; } + void setAlignment(Qt::Alignment align); + + qreal margins() const { return m_defaultMargins; } + void setMargins(qreal m); + + qreal leftMargin() const { return m_isLeftMarginSet ? m_margins.left() : m_defaultMargins; } + void setLeftMargin(qreal m); + void resetLeftMargin(); + + qreal topMargin() const { return m_isTopMarginSet ? m_margins.top() : m_defaultMargins; } + void setTopMargin(qreal m); + void resetTopMargin(); + + qreal rightMargin() const { return m_isRightMarginSet ? m_margins.right() : m_defaultMargins; } + void setRightMargin(qreal m); + void resetRightMargin(); + + qreal bottomMargin() const { return m_isBottomMarginSet ? m_margins.bottom() : m_defaultMargins; } + void setBottomMargin(qreal m); + void resetBottomMargin(); + + QMarginsF qMargins() const { + return QMarginsF(leftMargin(), topMargin(), rightMargin(), bottomMargin()); + } + + bool setChangesNotificationEnabled(bool enabled) + { + const bool old = m_changesNotificationEnabled; + m_changesNotificationEnabled = enabled; + return old; + } + + qreal sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const; + + bool isExtentExplicitlySet(Qt::Orientation o, Qt::SizeHint whichSize) const + { + switch (whichSize) { + case Qt::MinimumSize: + return o == Qt::Horizontal ? m_isMinimumWidthSet : m_isMinimumHeightSet; + case Qt::MaximumSize: + return o == Qt::Horizontal ? m_isMaximumWidthSet : m_isMaximumHeightSet; + case Qt::PreferredSize: + return true; // Layout.preferredWidth is always explicitly set + case Qt::MinimumDescent: // Not supported + case Qt::NSizeHints: + return false; + } + return false; + } + +signals: + void minimumWidthChanged(); + void minimumHeightChanged(); + void preferredWidthChanged(); + void preferredHeightChanged(); + void maximumWidthChanged(); + void maximumHeightChanged(); + void fillWidthChanged(); + void fillHeightChanged(); + void leftMarginChanged(); + void topMarginChanged(); + void rightMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void rowChanged(); + void columnChanged(); + void rowSpanChanged(); + void columnSpanChanged(); + void alignmentChanged(); + +private: + void invalidateItem(); + void repopulateLayout(); + QQuickLayout *parentLayout() const; + QQuickItem *item() const; +private: + qreal m_minimumWidth; + qreal m_minimumHeight; + qreal m_preferredWidth; + qreal m_preferredHeight; + qreal m_maximumWidth; + qreal m_maximumHeight; + + qreal m_defaultMargins; + QMarginsF m_margins; + + // GridLayout specific properties + int m_row; + int m_column; + int m_rowSpan; + int m_columnSpan; + + unsigned m_fillWidth : 1; + unsigned m_fillHeight : 1; + unsigned m_isFillWidthSet : 1; + unsigned m_isFillHeightSet : 1; + unsigned m_isMinimumWidthSet : 1; + unsigned m_isMinimumHeightSet : 1; + // preferredWidth and preferredHeight are always explicit, since + // their implicit equivalent is implicitWidth and implicitHeight + unsigned m_isMaximumWidthSet : 1; + unsigned m_isMaximumHeightSet : 1; + unsigned m_changesNotificationEnabled : 1; + unsigned m_isLeftMarginSet : 1; + unsigned m_isTopMarginSet : 1; + unsigned m_isRightMarginSet : 1; + unsigned m_isBottomMarginSet : 1; + Qt::Alignment m_alignment; + friend class QQuickLayout; +}; + +inline QQuickLayoutAttached *attachedLayoutObject(QQuickItem *item, bool create = true) +{ + return static_cast<QQuickLayoutAttached *>(qmlAttachedPropertiesObject<QQuickLayout>(item, create)); +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickLayout) +QML_DECLARE_TYPEINFO(QQuickLayout, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKLAYOUT_P_H diff --git a/src/imports/layouts/qquicklayoutstyleinfo.cpp b/src/imports/layouts/qquicklayoutstyleinfo.cpp new file mode 100644 index 0000000000..c33ceffb2d --- /dev/null +++ b/src/imports/layouts/qquicklayoutstyleinfo.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/private/qfont_p.h> + +#include "qquicklayoutstyleinfo_p.h" + + +QT_BEGIN_NAMESPACE + +QQuickLayoutStyleInfo::QQuickLayoutStyleInfo() +{ +} + +qreal QQuickLayoutStyleInfo::spacing(Qt::Orientation /*orientation*/) const +{ +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_QNX) || defined(Q_OS_WINRT) + // On Android and iOS the default spacing between each UI element is 8dp + qreal spacing = 8.0; +#else + qreal spacing = 5.0; +#endif + +#ifndef Q_OS_OSX + // On OS X the DPI is always 72 so we should not scale it + spacing = qRound(spacing * (qreal(qt_defaultDpiX()) / 96.0)); +#endif + + return spacing; +} + +qreal QQuickLayoutStyleInfo::windowMargin(Qt::Orientation /*orientation*/) const +{ + return 0; +} + +bool QQuickLayoutStyleInfo::hasChangedCore() const +{ + // never changes + return false; +} + +QT_END_NAMESPACE + diff --git a/src/imports/layouts/qquicklayoutstyleinfo_p.h b/src/imports/layouts/qquicklayoutstyleinfo_p.h new file mode 100644 index 0000000000..ce86c2a37d --- /dev/null +++ b/src/imports/layouts/qquicklayoutstyleinfo_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLAYOUTSTYLEINFO_P_H +#define QQUICKLAYOUTSTYLEINFO_P_H + +#include <QtGui/private/qabstractlayoutstyleinfo_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickLayoutStyleInfo : public QAbstractLayoutStyleInfo +{ +public: + QQuickLayoutStyleInfo(); + + qreal spacing(Qt::Orientation orientation) const Q_DECL_OVERRIDE; + qreal windowMargin(Qt::Orientation orientation) const Q_DECL_OVERRIDE; + bool hasChangedCore() const Q_DECL_OVERRIDE; + +}; + +QT_END_NAMESPACE + +#endif // QQUICKLAYOUTSTYLEINFO_P_H diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp new file mode 100644 index 0000000000..2f8af4c58b --- /dev/null +++ b/src/imports/layouts/qquicklinearlayout.cpp @@ -0,0 +1,908 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklinearlayout_p.h" +#include "qquickgridlayoutengine_p.h" +#include "qquicklayoutstyleinfo_p.h" +#include <QtCore/qnumeric.h> +#include "qdebug.h" +#include <limits> + +/*! + \qmltype RowLayout + \instantiates QQuickRowLayout + \inherits Item + \inqmlmodule QtQuick.Layouts + \ingroup layouts + \brief Identical to \l GridLayout, but having only one row. + + It is available as a convenience for developers, as it offers a cleaner API. + + Items in a RowLayout support these attached properties: + \list + \li \l{Layout::minimumWidth}{Layout.minimumWidth} + \li \l{Layout::minimumHeight}{Layout.minimumHeight} + \li \l{Layout::preferredWidth}{Layout.preferredWidth} + \li \l{Layout::preferredHeight}{Layout.preferredHeight} + \li \l{Layout::maximumWidth}{Layout.maximumWidth} + \li \l{Layout::maximumHeight}{Layout.maximumHeight} + \li \l{Layout::fillWidth}{Layout.fillWidth} + \li \l{Layout::fillHeight}{Layout.fillHeight} + \li \l{Layout::alignment}{Layout.alignment} + \endlist + + \image rowlayout.png + + \code + RowLayout { + id: layout + anchors.fill: parent + spacing: 6 + Rectangle { + color: 'teal' + Layout.fillWidth: true + Layout.minimumWidth: 50 + Layout.preferredWidth: 100 + Layout.maximumWidth: 300 + Layout.minimumHeight: 150 + Text { + anchors.centerIn: parent + text: parent.width + 'x' + parent.height + } + } + Rectangle { + color: 'plum' + Layout.fillWidth: true + Layout.minimumWidth: 100 + Layout.preferredWidth: 200 + Layout.preferredHeight: 100 + Text { + anchors.centerIn: parent + text: parent.width + 'x' + parent.height + } + } + } + \endcode + + Read more about attached properties \l{QML Object Attributes}{here}. + \sa ColumnLayout + \sa GridLayout + \sa Row +*/ + +/*! + \qmltype ColumnLayout + \instantiates QQuickColumnLayout + \inherits Item + \inqmlmodule QtQuick.Layouts + \ingroup layouts + \brief Identical to \l GridLayout, but having only one column. + + It is available as a convenience for developers, as it offers a cleaner API. + + Items in a ColumnLayout support these attached properties: + \list + \li \l{Layout::minimumWidth}{Layout.minimumWidth} + \li \l{Layout::minimumHeight}{Layout.minimumHeight} + \li \l{Layout::preferredWidth}{Layout.preferredWidth} + \li \l{Layout::preferredHeight}{Layout.preferredHeight} + \li \l{Layout::maximumWidth}{Layout.maximumWidth} + \li \l{Layout::maximumHeight}{Layout.maximumHeight} + \li \l{Layout::fillWidth}{Layout.fillWidth} + \li \l{Layout::fillHeight}{Layout.fillHeight} + \li \l{Layout::alignment}{Layout.alignment} + \endlist + + \image columnlayout.png + + \code + ColumnLayout{ + spacing: 2 + + Rectangle { + Layout.alignment: Qt.AlignCenter + color: "red" + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + } + + Rectangle { + Layout.alignment: Qt.AlignRight + color: "green" + Layout.preferredWidth: 40 + Layout.preferredHeight: 70 + } + + Rectangle { + Layout.alignment: Qt.AlignBottom + Layout.fillHeight: true + color: "blue" + Layout.preferredWidth: 70 + Layout.preferredHeight: 40 + } + } + \endcode + + Read more about attached properties \l{QML Object Attributes}{here}. + + \sa RowLayout + \sa GridLayout + \sa Column +*/ + + +/*! + \qmltype GridLayout + \instantiates QQuickGridLayout + \inherits Item + \inqmlmodule QtQuick.Layouts + \ingroup layouts + \brief Provides a way of dynamically arranging items in a grid. + + + + If the GridLayout is resized, all items in the layout will be rearranged. It is similar + to the widget-based QGridLayout. All visible children of the GridLayout element will belong to + the layout. If you want a layout with just one row or one column, you can use the + \l RowLayout or \l ColumnLayout. These offer a bit more convenient API, and improve + readability. + + By default items will be arranged according to the \l flow property. The default value of + the \l flow property is \c GridLayout.LeftToRight. + + If the \l columns property is specified, it will be treated as a maximum limit of how many + columns the layout can have, before the auto-positioning wraps back to the beginning of the + next row. The \l columns property is only used when \l flow is \c GridLayout.LeftToRight. + + \image gridlayout.png + + \code + GridLayout { + id: grid + columns: 3 + + Text { text: "Three"; font.bold: true; } + Text { text: "words"; color: "red" } + Text { text: "in"; font.underline: true } + Text { text: "a"; font.pixelSize: 20 } + Text { text: "row"; font.strikeout: true } + } + \endcode + + The \l rows property works in a similar way, but items are auto-positioned vertically. The \l + rows property is only used when \l flow is \c GridLayout.TopToBottom. + + You can specify which cell you want an item to occupy by setting the + \l{Layout::row}{Layout.row} and \l{Layout::column}{Layout.column} properties. You can also + specify the row span or column span by setting the \l{Layout::rowSpan}{Layout.rowSpan} or + \l{Layout::columnSpan}{Layout.columnSpan} properties. + + + Items in a GridLayout support these attached properties: + \list + \li \l{Layout::row}{Layout.row} + \li \l{Layout::column}{Layout.column} + \li \l{Layout::rowSpan}{Layout.rowSpan} + \li \l{Layout::columnSpan}{Layout.columnSpan} + \li \l{Layout::minimumWidth}{Layout.minimumWidth} + \li \l{Layout::minimumHeight}{Layout.minimumHeight} + \li \l{Layout::preferredWidth}{Layout.preferredWidth} + \li \l{Layout::preferredHeight}{Layout.preferredHeight} + \li \l{Layout::maximumWidth}{Layout.maximumWidth} + \li \l{Layout::maximumHeight}{Layout.maximumHeight} + \li \l{Layout::fillWidth}{Layout.fillWidth} + \li \l{Layout::fillHeight}{Layout.fillHeight} + \li \l{Layout::alignment}{Layout.alignment} + \endlist + + Read more about attached properties \l{QML Object Attributes}{here}. + + \sa RowLayout + \sa ColumnLayout + \sa Grid +*/ + + + +QT_BEGIN_NAMESPACE + +QQuickGridLayoutBase::QQuickGridLayoutBase() + : QQuickLayout(*new QQuickGridLayoutBasePrivate) +{ + +} + +QQuickGridLayoutBase::QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd, + Qt::Orientation orientation, + QQuickItem *parent /*= 0*/) + : QQuickLayout(dd, parent) +{ + Q_D(QQuickGridLayoutBase); + d->orientation = orientation; + d->styleInfo = new QQuickLayoutStyleInfo; +} + +Qt::Orientation QQuickGridLayoutBase::orientation() const +{ + Q_D(const QQuickGridLayoutBase); + return d->orientation; +} + +void QQuickGridLayoutBase::setOrientation(Qt::Orientation orientation) +{ + Q_D(QQuickGridLayoutBase); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + invalidate(); +} + +QSizeF QQuickGridLayoutBase::sizeHint(Qt::SizeHint whichSizeHint) const +{ + Q_D(const QQuickGridLayoutBase); + return d->engine.sizeHint(whichSizeHint, QSizeF(), d->styleInfo); +} + +/*! + \qmlproperty enumeration GridLayout::layoutDirection + \since QtQuick.Layouts 1.1 + + This property holds the layout direction of the grid layout - it controls whether items are + laid out from left to right or right to left. If \c Qt.RightToLeft is specified, + left-aligned items will be right-aligned and right-aligned items will be left-aligned. + + Possible values: + + \list + \li Qt.LeftToRight (default) - Items are laid out from left to right. + \li Qt.RightToLeft - Items are laid out from right to left. + \endlist + + \sa RowLayout::layoutDirection, ColumnLayout::layoutDirection +*/ +Qt::LayoutDirection QQuickGridLayoutBase::layoutDirection() const +{ + Q_D(const QQuickGridLayoutBase); + return d->m_layoutDirection; +} + +void QQuickGridLayoutBase::setLayoutDirection(Qt::LayoutDirection dir) +{ + Q_D(QQuickGridLayoutBase); + d->m_layoutDirection = dir; + invalidate(); +} + +Qt::LayoutDirection QQuickGridLayoutBase::effectiveLayoutDirection() const +{ + Q_D(const QQuickGridLayoutBase); + return !d->effectiveLayoutMirror == (layoutDirection() == Qt::LeftToRight) + ? Qt::LeftToRight : Qt::RightToLeft; +} + +void QQuickGridLayoutBase::setAlignment(QQuickItem *item, Qt::Alignment alignment) +{ + Q_D(QQuickGridLayoutBase); + d->engine.setAlignment(item, alignment); +} + +QQuickGridLayoutBase::~QQuickGridLayoutBase() +{ + Q_D(QQuickGridLayoutBase); + + /* Avoid messy deconstruction, should give: + * Faster deconstruction + * Less risk of signals reaching already deleted objects + */ + for (int i = 0; i < itemCount(); ++i) { + QQuickItem *item = itemAt(i); + QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); + QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); + QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem())); + QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem())); + } + delete d->styleInfo; +} + +void QQuickGridLayoutBase::componentComplete() +{ + quickLayoutDebug() << objectName() << "QQuickGridLayoutBase::componentComplete()" << parent(); + QQuickLayout::componentComplete(); + updateLayoutItems(); + + QQuickItem *par = parentItem(); + if (qobject_cast<QQuickLayout*>(par)) + return; + rearrange(QSizeF(width(), height())); +} + +/* + Invalidation happens like this as a reaction to that a size hint changes on an item "a": + + Suppose we have the following Qml document: + RowLayout { + id: l1 + RowLayout { + id: l2 + Item { + id: a + } + Item { + id: b + } + } + } + + 1. l2->invalidateChildItem(a) is called on l2, where item refers to "a". + (this will dirty the cached size hints of item "a") + 2. l2->invalidate() is called + this will : + i) invalidate the layout engine + ii) dirty the cached size hints of item "l2" (by calling parentLayout()->invalidateChildItem + + */ +/*! + \internal + + Invalidates \a childItem and this layout. + After a call to invalidate, the next call to retrieve e.g. sizeHint will be up-to date. + This function will also call QQuickLayout::invalidate(0), to ensure that the parent layout + is invalidated. + */ +void QQuickGridLayoutBase::invalidate(QQuickItem *childItem) +{ + Q_D(QQuickGridLayoutBase); + if (!isReady()) + return; + if (d->m_rearranging) { + d->m_invalidateAfterRearrange << childItem; + return; + } + + quickLayoutDebug() << "QQuickGridLayoutBase::invalidate()"; + + if (childItem) { + if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem)) + layoutItem->invalidate(); + if (d->m_ignoredItems.contains(childItem)) { + updateLayoutItems(); + return; + } + } + // invalidate engine + d->engine.invalidate(); + + QQuickLayout::invalidate(this); + + QQuickLayoutAttached *info = attachedLayoutObject(this); + + const QSizeF min = sizeHint(Qt::MinimumSize); + const QSizeF pref = sizeHint(Qt::PreferredSize); + const QSizeF max = sizeHint(Qt::MaximumSize); + + const bool old = info->setChangesNotificationEnabled(false); + info->setMinimumImplicitSize(min); + info->setMaximumImplicitSize(max); + info->setChangesNotificationEnabled(old); + if (pref.width() == implicitWidth() && pref.height() == implicitHeight()) { + // In case setImplicitSize does not emit implicit{Width|Height}Changed + if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem())) + parentLayout->invalidate(this); + } else { + setImplicitSize(pref.width(), pref.height()); + } +} + +void QQuickGridLayoutBase::updateLayoutItems() +{ + Q_D(QQuickGridLayoutBase); + if (!isReady()) + return; + if (d->m_rearranging) { + d->m_updateAfterRearrange = true; + return; + } + + quickLayoutDebug() << "QQuickGridLayoutBase::updateLayoutItems"; + d->engine.deleteItems(); + insertLayoutItems(); + + invalidate(); + quickLayoutDebug() << "QQuickGridLayoutBase::updateLayoutItems LEAVING"; +} + +QQuickItem *QQuickGridLayoutBase::itemAt(int index) const +{ + Q_D(const QQuickGridLayoutBase); + return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem(); +} + +int QQuickGridLayoutBase::itemCount() const +{ + Q_D(const QQuickGridLayoutBase); + return d->engine.itemCount(); +} + +void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == ItemChildAddedChange) { + quickLayoutDebug() << "ItemChildAddedChange"; + QQuickItem *item = value.item; + QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); + QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); + } else if (change == ItemChildRemovedChange) { + quickLayoutDebug() << "ItemChildRemovedChange"; + QQuickItem *item = value.item; + QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); + QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); + } + + QQuickLayout::itemChange(change, value); +} + +void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem) +{ + Q_D(QQuickGridLayoutBase); + const int index = gridItem->firstRow(d->orientation); + d->engine.removeItem(gridItem); + d->engine.removeRows(index, 1, d->orientation); +} + +void QQuickGridLayoutBase::onItemVisibleChanged() +{ + if (!isReady()) + return; + quickLayoutDebug() << "QQuickGridLayoutBase::onItemVisibleChanged"; + updateLayoutItems(); +} + +void QQuickGridLayoutBase::onItemDestroyed() +{ + if (!isReady()) + return; + Q_D(QQuickGridLayoutBase); + quickLayoutDebug() << "QQuickGridLayoutBase::onItemDestroyed"; + QQuickItem *inDestruction = static_cast<QQuickItem *>(sender()); + if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(inDestruction)) { + removeGridItem(gridItem); + delete gridItem; + invalidate(); + } +} + +void QQuickGridLayoutBase::rearrange(const QSizeF &size) +{ + Q_D(QQuickGridLayoutBase); + if (!isReady()) + return; + + d->m_rearranging = true; + quickLayoutDebug() << objectName() << "QQuickGridLayoutBase::rearrange()" << size; + Qt::LayoutDirection visualDir = effectiveLayoutDirection(); + d->engine.setVisualDirection(visualDir); + + /* + qreal left, top, right, bottom; + left = top = right = bottom = 0; // ### support for margins? + if (visualDir == Qt::RightToLeft) + qSwap(left, right); + */ + + // Set m_dirty to false in case size hint changes during arrangement. + // This could happen if there is a binding like implicitWidth: height + QQuickLayout::rearrange(size); + d->engine.setGeometries(QRectF(QPointF(0,0), size), d->styleInfo); + d->m_rearranging = false; + + for (QQuickItem *invalid : qAsConst(d->m_invalidateAfterRearrange)) + invalidate(invalid); + d->m_invalidateAfterRearrange.clear(); + + if (d->m_updateAfterRearrange) { + updateLayoutItems(); + d->m_updateAfterRearrange = false; + } +} + +/********************************** + ** + ** QQuickGridLayout + ** + **/ +QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = 0*/) + : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent) +{ +} + +/*! + \qmlproperty real GridLayout::columnSpacing + + This property holds the spacing between each column. + The default value is \c 5. +*/ +qreal QQuickGridLayout::columnSpacing() const +{ + Q_D(const QQuickGridLayout); + return d->engine.spacing(Qt::Horizontal, d->styleInfo); +} + +void QQuickGridLayout::setColumnSpacing(qreal spacing) +{ + Q_D(QQuickGridLayout); + if (qIsNaN(spacing) || columnSpacing() == spacing) + return; + + d->engine.setSpacing(spacing, Qt::Horizontal); + invalidate(); +} + +/*! + \qmlproperty real GridLayout::rowSpacing + + This property holds the spacing between each row. + The default value is \c 5. +*/ +qreal QQuickGridLayout::rowSpacing() const +{ + Q_D(const QQuickGridLayout); + return d->engine.spacing(Qt::Vertical, d->styleInfo); +} + +void QQuickGridLayout::setRowSpacing(qreal spacing) +{ + Q_D(QQuickGridLayout); + if (qIsNaN(spacing) || rowSpacing() == spacing) + return; + + d->engine.setSpacing(spacing, Qt::Vertical); + invalidate(); +} + +/*! + \qmlproperty int GridLayout::columns + + This property holds the column limit for items positioned if \l flow is + \c GridLayout.LeftToRight. + The default value is that there is no limit. +*/ +int QQuickGridLayout::columns() const +{ + Q_D(const QQuickGridLayout); + return d->columns; +} + +void QQuickGridLayout::setColumns(int columns) +{ + Q_D(QQuickGridLayout); + if (d->columns == columns) + return; + d->columns = columns; + updateLayoutItems(); + emit columnsChanged(); +} + + +/*! + \qmlproperty int GridLayout::rows + + This property holds the row limit for items positioned if \l flow is \c GridLayout.TopToBottom. + The default value is that there is no limit. +*/ +int QQuickGridLayout::rows() const +{ + Q_D(const QQuickGridLayout); + return d->rows; +} + +void QQuickGridLayout::setRows(int rows) +{ + Q_D(QQuickGridLayout); + if (d->rows == rows) + return; + d->rows = rows; + updateLayoutItems(); + emit rowsChanged(); +} + + +/*! + \qmlproperty enumeration GridLayout::flow + + This property holds the flow direction of items that does not have an explicit cell + position set. + It is used together with the \l columns or \l rows property, where + they specify when flow is reset to the next row or column respectively. + + Possible values are: + + \list + \li GridLayout.LeftToRight (default) - Items are positioned next to + each other, then wrapped to the next line. + \li GridLayout.TopToBottom - Items are positioned next to each + other from top to bottom, then wrapped to the next column. + \endlist + + \sa rows + \sa columns +*/ +QQuickGridLayout::Flow QQuickGridLayout::flow() const +{ + Q_D(const QQuickGridLayout); + return d->flow; +} + +void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow) +{ + Q_D(QQuickGridLayout); + if (d->flow == flow) + return; + d->flow = flow; + // If flow is changed, the layout needs to be repopulated + updateLayoutItems(); + emit flowChanged(); +} + +void QQuickGridLayout::insertLayoutItems() +{ + Q_D(QQuickGridLayout); + + int nextCellPos[2] = {0,0}; + int &nextColumn = nextCellPos[0]; + int &nextRow = nextCellPos[1]; + + const int flowOrientation = flow(); + int &flowColumn = nextCellPos[flowOrientation]; + int &flowRow = nextCellPos[1 - flowOrientation]; + int flowBound = (flowOrientation == QQuickGridLayout::LeftToRight) ? columns() : rows(); + + if (flowBound < 0) + flowBound = std::numeric_limits<int>::max(); + + d->m_ignoredItems.clear(); + QSizeF sizeHints[Qt::NSizeHints]; + const auto items = childItems(); + for (QQuickItem *child : items) { + QQuickLayoutAttached *info = 0; + + // Will skip all items with effective maximum width/height == 0 + if (shouldIgnoreItem(child, info, sizeHints)) + continue; + + Qt::Alignment alignment = 0; + int row = -1; + int column = -1; + int span[2] = {1,1}; + int &columnSpan = span[0]; + int &rowSpan = span[1]; + + bool invalidRowColumn = false; + if (info) { + if (info->isRowSet() || info->isColumnSet()) { + // If row is specified and column is not specified (or vice versa), + // the unspecified component of the cell position should default to 0 + row = column = 0; + if (info->isRowSet()) { + row = info->row(); + invalidRowColumn = row < 0; + } + if (info->isColumnSet()) { + column = info->column(); + invalidRowColumn = column < 0; + } + } + if (invalidRowColumn) { + qWarning("QQuickGridLayoutBase::insertLayoutItems: invalid row/column: %d", + row < 0 ? row : column); + return; + } + rowSpan = info->rowSpan(); + columnSpan = info->columnSpan(); + if (columnSpan < 1 || rowSpan < 1) { + qWarning("QQuickGridLayoutBase::addItem: invalid row span/column span: %d", + rowSpan < 1 ? rowSpan : columnSpan); + return; + } + + alignment = info->alignment(); + } + + Q_ASSERT(columnSpan >= 1); + Q_ASSERT(rowSpan >= 1); + const int sp = span[flowOrientation]; + if (sp > flowBound) + return; + + if (row >= 0) + nextRow = row; + if (column >= 0) + nextColumn = column; + + if (row < 0 || column < 0) { + /* if row or column is not specified, find next position by + advancing in the flow direction until there is a cell that + can accept the item. + + The acceptance rules are pretty simple, but complexity arises + when an item requires several cells (due to spans): + 1. Check if the cells that the item will require + does not extend beyond columns (for LeftToRight) or + rows (for TopToBottom). + 2. Check if the cells that the item will require is not already + taken by another item. + */ + bool cellAcceptsItem; + while (true) { + // Check if the item does not span beyond the layout bound + cellAcceptsItem = (flowColumn + sp) <= flowBound; + + // Check if all the required cells are not taken + for (int rs = 0; cellAcceptsItem && rs < rowSpan; ++rs) { + for (int cs = 0; cellAcceptsItem && cs < columnSpan; ++cs) { + if (d->engine.itemAt(nextRow + rs, nextColumn + cs)) { + cellAcceptsItem = false; + } + } + } + if (cellAcceptsItem) + break; + ++flowColumn; + if (flowColumn == flowBound) { + flowColumn = 0; + ++flowRow; + } + } + } + column = nextColumn; + row = nextRow; + QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, row, column, rowSpan, columnSpan, alignment); + layoutItem->setCachedSizeHints(sizeHints); + + d->engine.insertItem(layoutItem, -1); + } +} + +/********************************** + ** + ** QQuickLinearLayout + ** + **/ +QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation, + QQuickItem *parent /*= 0*/) + : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent) +{ +} + +/*! + \qmlproperty enumeration RowLayout::layoutDirection + \since QtQuick.Layouts 1.1 + + This property holds the layout direction of the row layout - it controls whether items are laid + out from left ro right or right to left. If \c Qt.RightToLeft is specified, + left-aligned items will be right-aligned and right-aligned items will be left-aligned. + + Possible values: + + \list + \li Qt.LeftToRight (default) - Items are laid out from left to right. + \li Qt.RightToLeft - Items are laid out from right to left + \endlist + + \sa GridLayout::layoutDirection, ColumnLayout::layoutDirection +*/ +/*! + \qmlproperty enumeration ColumnLayout::layoutDirection + \since QtQuick.Layouts 1.1 + + This property holds the layout direction of the column layout - it controls whether items are laid + out from left ro right or right to left. If \c Qt.RightToLeft is specified, + left-aligned items will be right-aligned and right-aligned items will be left-aligned. + + Possible values: + + \list + \li Qt.LeftToRight (default) - Items are laid out from left to right. + \li Qt.RightToLeft - Items are laid out from right to left + \endlist + + \sa GridLayout::layoutDirection, RowLayout::layoutDirection +*/ + + +/*! + \qmlproperty real RowLayout::spacing + + This property holds the spacing between each cell. + The default value is \c 5. +*/ +/*! + \qmlproperty real ColumnLayout::spacing + + This property holds the spacing between each cell. + The default value is \c 5. +*/ + +qreal QQuickLinearLayout::spacing() const +{ + Q_D(const QQuickLinearLayout); + return d->engine.spacing(d->orientation, d->styleInfo); +} + +void QQuickLinearLayout::setSpacing(qreal space) +{ + Q_D(QQuickLinearLayout); + if (qIsNaN(space) || spacing() == space) + return; + + d->engine.setSpacing(space, Qt::Horizontal | Qt::Vertical); + invalidate(); +} + +void QQuickLinearLayout::insertLayoutItems() +{ + Q_D(QQuickLinearLayout); + d->m_ignoredItems.clear(); + QSizeF sizeHints[Qt::NSizeHints]; + const auto items = childItems(); + for (QQuickItem *child : items) { + Q_ASSERT(child); + QQuickLayoutAttached *info = 0; + + // Will skip all items with effective maximum width/height == 0 + if (shouldIgnoreItem(child, info, sizeHints)) + continue; + + Qt::Alignment alignment = 0; + if (info) + alignment = info->alignment(); + + const int index = d->engine.rowCount(d->orientation); + d->engine.insertRow(index, d->orientation); + + int gridRow = 0; + int gridColumn = index; + if (d->orientation == Qt::Vertical) + qSwap(gridRow, gridColumn); + QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment); + layoutItem->setCachedSizeHints(sizeHints); + d->engine.insertItem(layoutItem, index); + } +} + +QT_END_NAMESPACE diff --git a/src/imports/layouts/qquicklinearlayout_p.h b/src/imports/layouts/qquicklinearlayout_p.h new file mode 100644 index 0000000000..86404f8d79 --- /dev/null +++ b/src/imports/layouts/qquicklinearlayout_p.h @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLINEARLAYOUT_P_H +#define QQUICKLINEARLAYOUT_P_H + +#include "qquicklayout_p.h" +#include "qquickgridlayoutengine_p.h" + +QT_BEGIN_NAMESPACE + +/********************************** + ** + ** QQuickGridLayoutBase + ** + **/ +class QQuickGridLayoutBasePrivate; + +class QQuickGridLayoutBase : public QQuickLayout +{ + Q_OBJECT + + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) + +public: + + QQuickGridLayoutBase(); + + explicit QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd, + Qt::Orientation orientation, + QQuickItem *parent = 0); + + ~QQuickGridLayoutBase(); + void componentComplete() Q_DECL_OVERRIDE; + void invalidate(QQuickItem *childItem = 0) Q_DECL_OVERRIDE; + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + QSizeF sizeHint(Qt::SizeHint whichSizeHint) const Q_DECL_OVERRIDE; + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection dir); + Qt::LayoutDirection effectiveLayoutDirection() const; + void setAlignment(QQuickItem *item, Qt::Alignment align) Q_DECL_OVERRIDE; + +protected: + void updateLayoutItems() Q_DECL_OVERRIDE; + QQuickItem *itemAt(int index) const Q_DECL_OVERRIDE; + int itemCount() const Q_DECL_OVERRIDE; + + void rearrange(const QSizeF &size) Q_DECL_OVERRIDE; + virtual void insertLayoutItems() {} + void itemChange(ItemChange change, const ItemChangeData &data) Q_DECL_OVERRIDE; + +signals: + Q_REVISION(1) void layoutDirectionChanged(); + +protected slots: + void onItemVisibleChanged(); + void onItemDestroyed(); + +private: + void removeGridItem(QGridLayoutItem *gridItem); + Q_DECLARE_PRIVATE(QQuickGridLayoutBase) +}; + +class QQuickLayoutStyleInfo; + +class QQuickGridLayoutBasePrivate : public QQuickLayoutPrivate +{ + Q_DECLARE_PUBLIC(QQuickGridLayoutBase) + +public: + QQuickGridLayoutBasePrivate() : m_rearranging(false) + , m_updateAfterRearrange(false) + , m_layoutDirection(Qt::LeftToRight) + {} + + void mirrorChange() Q_DECL_OVERRIDE + { + Q_Q(QQuickGridLayoutBase); + q->invalidate(); + } + + QQuickGridLayoutEngine engine; + Qt::Orientation orientation; + unsigned m_rearranging : 1; + unsigned m_updateAfterRearrange : 1; + QVector<QQuickItem *> m_invalidateAfterRearrange; + Qt::LayoutDirection m_layoutDirection : 2; + + QQuickLayoutStyleInfo *styleInfo; +}; + +/********************************** + ** + ** QQuickGridLayout + ** + **/ +class QQuickGridLayoutPrivate; +class QQuickGridLayout : public QQuickGridLayoutBase +{ + Q_OBJECT + + Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged) + Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged) + Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged) + Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) +public: + explicit QQuickGridLayout(QQuickItem *parent = 0); + qreal columnSpacing() const; + void setColumnSpacing(qreal spacing); + qreal rowSpacing() const; + void setRowSpacing(qreal spacing); + + int columns() const; + void setColumns(int columns); + int rows() const; + void setRows(int rows); + + Q_ENUMS(Flow) + enum Flow { LeftToRight, TopToBottom }; + Flow flow() const; + void setFlow(Flow flow); + + void insertLayoutItems(); + +signals: + void columnSpacingChanged(); + void rowSpacingChanged(); + + void columnsChanged(); + void rowsChanged(); + + void flowChanged(); +private: + Q_DECLARE_PRIVATE(QQuickGridLayout) +}; + +class QQuickGridLayoutPrivate : public QQuickGridLayoutBasePrivate +{ + Q_DECLARE_PUBLIC(QQuickGridLayout) +public: + QQuickGridLayoutPrivate(): columns(-1), rows(-1), flow(QQuickGridLayout::LeftToRight) {} + int columns; + int rows; + QQuickGridLayout::Flow flow; +}; + + +/********************************** + ** + ** QQuickLinearLayout + ** + **/ +class QQuickLinearLayoutPrivate; +class QQuickLinearLayout : public QQuickGridLayoutBase +{ + Q_OBJECT + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) +public: + explicit QQuickLinearLayout(Qt::Orientation orientation, + QQuickItem *parent = 0); + void insertLayoutItem(QQuickItem *item); + qreal spacing() const; + void setSpacing(qreal spacing); + + void insertLayoutItems(); + +signals: + void spacingChanged(); +private: + Q_DECLARE_PRIVATE(QQuickLinearLayout) +}; + +class QQuickLinearLayoutPrivate : public QQuickGridLayoutBasePrivate +{ + Q_DECLARE_PUBLIC(QQuickLinearLayout) +public: + QQuickLinearLayoutPrivate() {} +}; + + +/********************************** + ** + ** QQuickRowLayout + ** + **/ +class QQuickRowLayout : public QQuickLinearLayout +{ + Q_OBJECT + +public: + explicit QQuickRowLayout(QQuickItem *parent = 0) + : QQuickLinearLayout(Qt::Horizontal, parent) {} +}; + + +/********************************** + ** + ** QQuickColumnLayout + ** + **/ +class QQuickColumnLayout : public QQuickLinearLayout +{ + Q_OBJECT + +public: + explicit QQuickColumnLayout(QQuickItem *parent = 0) + : QQuickLinearLayout(Qt::Vertical, parent) {} +}; + +QT_END_NAMESPACE + +#endif // QQUICKLINEARLAYOUT_P_H diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp new file mode 100644 index 0000000000..a223dd0374 --- /dev/null +++ b/src/imports/layouts/qquickstacklayout.cpp @@ -0,0 +1,339 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstacklayout_p.h" +#include <limits> + +/*! + \qmltype StackLayout + \instantiates QQuickStackLayout + \inherits Item + \inqmlmodule QtQuick.Layouts + \ingroup layouts + \brief The StackLayout class provides a stack of items where + only one item is visible at a time. + + The current visible item can be modified by setting the \l currentIndex property. + The index corresponds to the order of the StackLayout's children. + + In contrast to most other layouts, child Items' \l{Layout::fillWidth}{Layout.fillWidth} and \l{Layout::fillHeight}{Layout.fillHeight} properties + default to \c true. As a consequence, child items are by default filled to match the size of the StackLayout as long as their + \l{Layout::maximumWidth}{Layout.maximumWidth} or \l{Layout::maximumHeight}{Layout.maximumHeight} does not prevent it. + + Items are added to the layout by reparenting the item to the layout. Similarly, removal is done by reparenting the item from the layout. + Both of these operations will affect the layout's \l count property. + + The following code will create a StackLayout where only the 'plum' rectangle is visible. + \code + StackLayout { + id: layout + anchors.fill: parent + currentIndex: 1 + Rectangle { + color: 'teal' + implicitWidth: 200 + implicitHeight: 200 + } + Rectangle { + color: 'plum' + implicitWidth: 300 + implicitHeight: 200 + } + } + \endcode + + Items in a StackLayout support these attached properties: + \list + \li \l{Layout::minimumWidth}{Layout.minimumWidth} + \li \l{Layout::minimumHeight}{Layout.minimumHeight} + \li \l{Layout::preferredWidth}{Layout.preferredWidth} + \li \l{Layout::preferredHeight}{Layout.preferredHeight} + \li \l{Layout::maximumWidth}{Layout.maximumWidth} + \li \l{Layout::maximumHeight}{Layout.maximumHeight} + \li \l{Layout::fillWidth}{Layout.fillWidth} + \li \l{Layout::fillHeight}{Layout.fillHeight} + \endlist + + Read more about attached properties \l{QML Object Attributes}{here}. + \sa ColumnLayout + \sa GridLayout + \sa RowLayout + \sa StackView +*/ + +QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) : + QQuickLayout(*new QQuickStackLayoutPrivate, parent) +{ +} + +/*! + \qmlproperty int StackLayout::count + + This property holds the number of items that belong to the layout. + + Only items that are children of the StackLayout will be candidates for layouting. +*/ +int QQuickStackLayout::count() const +{ + Q_D(const QQuickStackLayout); + return d->count; +} + +/*! + \qmlproperty int StackLayout::currentIndex + + This property holds the index of the child item that is currently visible in the StackLayout. + By default it will be \c -1 for an empty layout, otherwise the default is \c 0 (referring to the first item). +*/ +int QQuickStackLayout::currentIndex() const +{ + Q_D(const QQuickStackLayout); + return d->currentIndex; +} + +void QQuickStackLayout::setCurrentIndex(int index) +{ + Q_D(QQuickStackLayout); + if (index != d->currentIndex) { + QQuickItem *prev = itemAt(d->currentIndex); + QQuickItem *next = itemAt(index); + d->currentIndex = index; + d->explicitCurrentIndex = true; + if (prev) + prev->setVisible(false); + if (next) + next->setVisible(true); + + if (isComponentComplete()) { + rearrange(QSizeF(width(), height())); + emit currentIndexChanged(); + } + } +} + +void QQuickStackLayout::componentComplete() +{ + QQuickLayout::componentComplete(); // will call our geometryChange(), (where isComponentComplete() == true) + + updateLayoutItems(); + + QQuickItem *par = parentItem(); + if (qobject_cast<QQuickLayout*>(par)) + return; + + rearrange(QSizeF(width(), height())); +} + +QSizeF QQuickStackLayout::sizeHint(Qt::SizeHint whichSizeHint) const +{ + QSizeF &askingFor = m_cachedSizeHints[whichSizeHint]; + if (!askingFor.isValid()) { + QSizeF &minS = m_cachedSizeHints[Qt::MinimumSize]; + QSizeF &prefS = m_cachedSizeHints[Qt::PreferredSize]; + QSizeF &maxS = m_cachedSizeHints[Qt::MaximumSize]; + + minS = QSizeF(0,0); + prefS = QSizeF(0,0); + maxS = QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity()); + + const int count = itemCount(); + m_cachedItemSizeHints.resize(count); + for (int i = 0; i < count; ++i) { + SizeHints &hints = m_cachedItemSizeHints[i]; + QQuickStackLayout::collectItemSizeHints(itemAt(i), hints.array); + minS = minS.expandedTo(hints.min()); + prefS = prefS.expandedTo(hints.pref()); + //maxS = maxS.boundedTo(hints.max()); // Can be resized to be larger than any of its items. + // This is the same as QStackLayout does it. + // Not sure how descent makes sense here... + } + } + return askingFor; +} + +int QQuickStackLayout::indexOf(QQuickItem *childItem) const +{ + if (childItem) { + int indexOfItem = 0; + const auto items = childItems(); + for (QQuickItem *item : items) { + if (shouldIgnoreItem(item)) + continue; + if (childItem == item) + return indexOfItem; + ++indexOfItem; + } + } + return -1; +} + +QQuickItem *QQuickStackLayout::itemAt(int index) const +{ + const auto items = childItems(); + for (QQuickItem *item : items) { + if (shouldIgnoreItem(item)) + continue; + if (index == 0) + return item; + --index; + } + return 0; +} + +int QQuickStackLayout::itemCount() const +{ + int count = 0; + const auto items = childItems(); + for (QQuickItem *item : items) { + if (shouldIgnoreItem(item)) + continue; + ++count; + } + return count; +} + +void QQuickStackLayout::setAlignment(QQuickItem * /*item*/, Qt::Alignment /*align*/) +{ + // ### Do we have to respect alignment? +} + +void QQuickStackLayout::invalidate(QQuickItem *childItem) +{ + Q_D(QQuickStackLayout); + if (d->m_ignoredItems.contains(childItem)) { + // If an invalid item gets a valid size, it should be included, as it was added to the layout + updateLayoutItems(); + return; + } + + const int indexOfChild = indexOf(childItem); + if (indexOfChild >= 0 && indexOfChild < m_cachedItemSizeHints.count()) { + m_cachedItemSizeHints[indexOfChild].min() = QSizeF(); + m_cachedItemSizeHints[indexOfChild].pref() = QSizeF(); + m_cachedItemSizeHints[indexOfChild].max() = QSizeF(); + } + + for (int i = 0; i < Qt::NSizeHints; ++i) + m_cachedSizeHints[i] = QSizeF(); + QQuickLayout::invalidate(this); + + QQuickLayoutAttached *info = attachedLayoutObject(this); + + const QSizeF min = sizeHint(Qt::MinimumSize); + const QSizeF pref = sizeHint(Qt::PreferredSize); + const QSizeF max = sizeHint(Qt::MaximumSize); + + const bool old = info->setChangesNotificationEnabled(false); + info->setMinimumImplicitSize(min); + info->setMaximumImplicitSize(max); + info->setChangesNotificationEnabled(old); + if (pref.width() == implicitWidth() && pref.height() == implicitHeight()) { + // In case setImplicitSize does not emit implicit{Width|Height}Changed + if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem())) + parentLayout->invalidate(this); + } else { + setImplicitSize(pref.width(), pref.height()); + } +} + +void QQuickStackLayout::updateLayoutItems() +{ + Q_D(QQuickStackLayout); + d->m_ignoredItems.clear(); + const int count = itemCount(); + int oldIndex = d->currentIndex; + if (!d->explicitCurrentIndex) + d->currentIndex = (count > 0 ? 0 : -1); + + if (d->currentIndex != oldIndex) + emit currentIndexChanged(); + + if (count != d->count) { + d->count = count; + emit countChanged(); + } + for (int i = 0; i < count; ++i) + itemAt(i)->setVisible(d->currentIndex == i); + + invalidate(); +} + +void QQuickStackLayout::rearrange(const QSizeF &newSize) +{ + Q_D(QQuickStackLayout); + if (newSize.isNull() || !newSize.isValid()) + return; + (void)sizeHint(Qt::PreferredSize); // Make sure m_cachedItemSizeHints are valid + + if (d->currentIndex == -1 || d->currentIndex >= m_cachedItemSizeHints.count()) + return; + QQuickStackLayout::SizeHints &hints = m_cachedItemSizeHints[d->currentIndex]; + QQuickItem *item = itemAt(d->currentIndex); + Q_ASSERT(item); + item->setPosition(QPointF(0,0)); // ### respect alignment? + item->setSize(newSize.expandedTo(hints.min()).boundedTo(hints.max())); + QQuickLayout::rearrange(newSize); +} + +void QQuickStackLayout::collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints) +{ + QQuickLayoutAttached *info = 0; + QQuickLayout::effectiveSizeHints_helper(item, sizeHints, &info, true); + if (!info) + return; + if (info->isFillWidthSet() && !info->fillWidth()) { + const qreal pref = sizeHints[Qt::PreferredSize].width(); + sizeHints[Qt::MinimumSize].setWidth(pref); + sizeHints[Qt::MaximumSize].setWidth(pref); + } + + if (info->isFillHeightSet() && !info->fillHeight()) { + const qreal pref = sizeHints[Qt::PreferredSize].height(); + sizeHints[Qt::MinimumSize].setHeight(pref); + sizeHints[Qt::MaximumSize].setHeight(pref); + } +} + +bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const +{ + const bool ignored = QQuickItemPrivate::get(item)->isTransparentForPositioner(); + if (ignored) + d_func()->m_ignoredItems << item; + return ignored; +} diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h new file mode 100644 index 0000000000..7b6400c3a3 --- /dev/null +++ b/src/imports/layouts/qquickstacklayout_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTACKLAYOUT_H +#define QQUICKSTACKLAYOUT_H + +#include <qquicklayout_p.h> + +class QQuickStackLayoutPrivate; + +class QQuickStackLayout : public QQuickLayout +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) + +public: + explicit QQuickStackLayout(QQuickItem *parent = 0); + int count() const; + int currentIndex() const; + void setCurrentIndex(int index); + + void componentComplete() Q_DECL_OVERRIDE; + QSizeF sizeHint(Qt::SizeHint whichSizeHint) const Q_DECL_OVERRIDE; + void setAlignment(QQuickItem *item, Qt::Alignment align) Q_DECL_OVERRIDE; + void invalidate(QQuickItem *childItem = 0) Q_DECL_OVERRIDE; + void updateLayoutItems() Q_DECL_OVERRIDE; + void rearrange(const QSizeF &) Q_DECL_OVERRIDE; + + // iterator + Q_INVOKABLE QQuickItem *itemAt(int index) const Q_DECL_OVERRIDE; + int itemCount() const Q_DECL_OVERRIDE; + int indexOf(QQuickItem *item) const; + + + +signals: + void currentIndexChanged(); + void countChanged(); + +public slots: + +private: + static void collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints); + bool shouldIgnoreItem(QQuickItem *item) const; + Q_DECLARE_PRIVATE(QQuickStackLayout) + + QList<QQuickItem*> m_items; + + typedef struct { + inline QSizeF &min() { return array[Qt::MinimumSize]; } + inline QSizeF &pref() { return array[Qt::PreferredSize]; } + inline QSizeF &max() { return array[Qt::MaximumSize]; } + QSizeF array[Qt::NSizeHints]; + } SizeHints; + + mutable QVector<SizeHints> m_cachedItemSizeHints; + mutable QSizeF m_cachedSizeHints[Qt::NSizeHints]; +}; + +class QQuickStackLayoutPrivate : public QQuickLayoutPrivate +{ + Q_DECLARE_PUBLIC(QQuickStackLayout) +public: + QQuickStackLayoutPrivate() : count(0), currentIndex(-1), explicitCurrentIndex(false) {} +private: + int count; + int currentIndex; + bool explicitCurrentIndex; +}; + +#endif // QQUICKSTACKLAYOUT_H diff --git a/src/particles/particles.pro b/src/particles/particles.pro index ba65ee3002..ab1c854253 100644 --- a/src/particles/particles.pro +++ b/src/particles/particles.pro @@ -1,4 +1,5 @@ TARGET = QtQuickParticles +MODULE = quickparticles CONFIG += internal_module @@ -13,7 +14,6 @@ exists("qqml_enable_gcov") { LIBS_PRIVATE += -lgcov } -MODULE = quickparticles -load(qt_module) - include(particles.pri) + +load(qt_module) diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp index 7e1a31f206..4d9f834492 100644 --- a/src/particles/qquickparticleemitter.cpp +++ b/src/particles/qquickparticleemitter.cpp @@ -343,7 +343,7 @@ void QQuickParticleEmitter::emitWindow(int timeStamp) { if (m_system == 0) return; - if ((!m_enabled || !m_particlesPerSecond)&& !m_pulseLeft && m_burstQueue.isEmpty()){ + if ((!m_enabled || m_particlesPerSecond <= 0)&& !m_pulseLeft && m_burstQueue.isEmpty()){ m_reset_last = true; return; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro index 3703d0fe0b..27b3a5b513 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro +++ b/src/plugins/qmltooling/qmldbg_debugger/qmldbg_debugger.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_debugger QT = qml-private core-private packetprotocol-private -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QQmlDebuggerServiceFactory -load(qt_plugin) - SOURCES += \ $$PWD/qdebugmessageservice.cpp \ $$PWD/qqmldebuggerservicefactory.cpp \ @@ -37,3 +33,6 @@ INCLUDEPATH += $$PWD \ OTHER_FILES += \ $$PWD/qqmldebuggerservice.json +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlDebuggerServiceFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro index c2ee733db6..a8844944e0 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro +++ b/src/plugins/qmltooling/qmldbg_inspector/qmldbg_inspector.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_inspector QT += qml-private quick-private core-private gui-private packetprotocol-private -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QQmlInspectorServiceFactory -load(qt_plugin) - INCLUDEPATH *= $$PWD $$PWD/../shared SOURCES += \ @@ -24,3 +20,7 @@ HEADERS += \ OTHER_FILES += \ qqmlinspectorservice.json + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlInspectorServiceFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro b/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro index 491be04b15..d731e47b7e 100644 --- a/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro +++ b/src/plugins/qmltooling/qmldbg_local/qmldbg_local.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_local QT = qml-private -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QLocalClientConnectionFactory -load(qt_plugin) - SOURCES += \ $$PWD/qlocalclientconnection.cpp @@ -18,3 +14,7 @@ INCLUDEPATH += $$PWD \ OTHER_FILES += \ $$PWD/qlocalclientconnection.json + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QLocalClientConnectionFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro b/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro index d490d77e50..b3fe1681e8 100644 --- a/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro +++ b/src/plugins/qmltooling/qmldbg_native/qmldbg_native.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_native QT += qml-private core-private packetprotocol-private -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QQmlNativeDebugConnectorFactory -load(qt_plugin) - HEADERS += \ $$PWD/../shared/qqmldebugpacket.h \ $$PWD/qqmlnativedebugconnector.h @@ -17,3 +13,7 @@ INCLUDEPATH += $$PWD \ OTHER_FILES += \ $$PWD/qqmlnativedebugconnector.json + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlNativeDebugConnectorFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro index 6efe9eacad..4629a7b81e 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro +++ b/src/plugins/qmltooling/qmldbg_profiler/qmldbg_profiler.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_profiler QT = qml-private core-private packetprotocol-private -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QQmlProfilerServiceFactory -load(qt_plugin) - SOURCES += \ $$PWD/qqmlenginecontrolservice.cpp \ $$PWD/qqmlprofileradapter.cpp \ @@ -27,3 +23,6 @@ INCLUDEPATH += $$PWD \ OTHER_FILES += \ $$PWD/qqmlprofilerservice.json +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlProfilerServiceFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro index 923faa01f3..fffdb4c888 100644 --- a/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro +++ b/src/plugins/qmltooling/qmldbg_server/qmldbg_server.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_server QT = qml-private packetprotocol-private -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QQmlDebugServerFactory -load(qt_plugin) - SOURCES += \ $$PWD/qqmldebugserver.cpp @@ -19,3 +15,7 @@ INCLUDEPATH += $$PWD \ OTHER_FILES += \ qqmldebugserver.json + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QQmlDebugServerFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro index fd419aeb56..1face1813e 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro +++ b/src/plugins/qmltooling/qmldbg_tcp/qmldbg_tcp.pro @@ -1,10 +1,6 @@ TARGET = qmldbg_tcp QT = qml-private network -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QTcpServerConnectionFactory -load(qt_plugin) - SOURCES += \ $$PWD/qtcpserverconnection.cpp @@ -18,3 +14,7 @@ INCLUDEPATH += $$PWD \ OTHER_FILES += \ $$PWD/qtcpserverconnection.json + +PLUGIN_TYPE = qmltooling +PLUGIN_CLASS_NAME = QTcpServerConnectionFactory +load(qt_plugin) diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro index 3728126dd9..7682e7b075 100644 --- a/src/plugins/qmltooling/qmltooling.pro +++ b/src/plugins/qmltooling/qmltooling.pro @@ -1,30 +1,32 @@ TEMPLATE = subdirs -# Utilities -SUBDIRS += \ - packetprotocol +!no_network { + # Utilities + SUBDIRS += \ + packetprotocol -# Connectors -SUBDIRS += \ - qmldbg_native \ - qmldbg_server \ + # Connectors + SUBDIRS += \ + qmldbg_native \ + qmldbg_server \ qmldbg_local \ qmldbg_tcp -# Services -SUBDIRS += \ - qmldbg_debugger \ - qmldbg_profiler + # Services + SUBDIRS += \ + qmldbg_debugger \ + qmldbg_profiler -qmldbg_server.depends = packetprotocol -qmldbg_native.depends = packetprotocol -qmldbg_debugger.depends = packetprotocol -qmldbg_profiler.depends = packetprotocol + qmldbg_server.depends = packetprotocol + qmldbg_native.depends = packetprotocol + qmldbg_debugger.depends = packetprotocol + qmldbg_profiler.depends = packetprotocol -qtHaveModule(quick) { - SUBDIRS += \ - qmldbg_inspector \ - qmldbg_quickprofiler - qmldbg_inspector.depends = packetprotocol - qmldbg_quickprofiler.depends = packetprotocol + qtHaveModule(quick) { + SUBDIRS += \ + qmldbg_inspector \ + qmldbg_quickprofiler + qmldbg_inspector.depends = packetprotocol + qmldbg_quickprofiler.depends = packetprotocol + } } diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 9fcfe53b05..a34c1cbf0e 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -411,7 +411,7 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen bool IRBuilder::isSignalPropertyName(const QString &name) { if (name.length() < 3) return false; - if (!name.startsWith(QStringLiteral("on"))) return false; + if (!name.startsWith(QLatin1String("on"))) return false; int ns = name.length(); for (int i = 2; i < ns; ++i) { const QChar curr = name.at(i); @@ -1061,7 +1061,7 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta Object *object = 0; if (!resolveQualifiedId(&name, &object)) return; - if (_object == object && name->name == QStringLiteral("id")) { + if (_object == object && name->name == QLatin1String("id")) { setId(name->identifierToken, value); return; } @@ -1097,7 +1097,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem, bool isOnAssignment) { - if (stringAt(propertyNameIndex) == QStringLiteral("id")) { + if (stringAt(propertyNameIndex) == QLatin1String("id")) { recordError(nameLocation, tr("Invalid component id specification")); return; } @@ -1192,7 +1192,7 @@ bool IRBuilder::resolveQualifiedId(QQmlJS::AST::UiQualifiedId **nameToResolve, O { QQmlJS::AST::UiQualifiedId *qualifiedIdElement = *nameToResolve; - if (qualifiedIdElement->name == QStringLiteral("id") && qualifiedIdElement->next) + if (qualifiedIdElement->name == QLatin1String("id") && qualifiedIdElement->next) COMPILE_EXCEPTION(qualifiedIdElement->identifierToken, tr( "Invalid use of id property")); // If it's a namespace, prepend the qualifier and we'll resolve it later to the correct type. @@ -1918,7 +1918,7 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis return d; } - if (name.endsWith(QStringLiteral("Changed"))) { + if (name.endsWith(QLatin1String("Changed"))) { QString propName = name.mid(0, name.length() - static_cast<int>(strlen("Changed"))); d = property(propName, notInRevision); diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 2b3cd93052..33716d57b8 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -972,7 +972,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio QmlIR::PropertyResolver resolver(propertyCache); - Q_ASSERT(propertyName.startsWith(QStringLiteral("on"))); + Q_ASSERT(propertyName.startsWith(QLatin1String("on"))); propertyName.remove(0, 2); // Note that the property name could start with any alpha or '_' or '$' character, @@ -1039,7 +1039,7 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio } QHash<QString, QStringList>::ConstIterator entry = customSignals.constFind(propertyName); - if (entry == customSignals.constEnd() && propertyName.endsWith(QStringLiteral("Changed"))) { + if (entry == customSignals.constEnd() && propertyName.endsWith(QLatin1String("Changed"))) { QString alternateName = propertyName.mid(0, propertyName.length() - static_cast<int>(strlen("Changed"))); entry = customSignals.constFind(alternateName); } @@ -1163,6 +1163,17 @@ struct StaticQtMetaObject : public QObject { return &staticQtMetaObject; } }; +bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QString &enumName, int enumValue, bool isQtObject) +{ + if (enumName.length() > 0 && enumName[0].isLower() && !isQtObject) { + COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName)); + } + binding->type = QV4::CompiledData::Binding::Type_Number; + binding->value.d = (double)enumValue; + binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + return true; +} + bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding) { bool isIntProp = (prop->propType == QMetaType::Int) && !prop->isEnum(); @@ -1185,6 +1196,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, return true; QHashedStringRef typeName(string.constData(), dot); + const bool isQtObject = (typeName == QLatin1String("Qt")); QString enumValue = string.mid(dot+1); if (isIntProp) { @@ -1192,16 +1204,15 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, bool ok; int enumval = evaluateEnum(typeName.toString(), enumValue.toUtf8(), &ok); if (ok) { - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = (double)enumval; - binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; + if (!assignEnumToBinding(binding, enumValue, enumval, isQtObject)) + return false; } return true; } QQmlType *type = 0; imports->resolveType(typeName, &type, 0, 0, 0); - if (!type && typeName != QLatin1String("Qt")) + if (!type && !isQtObject) return true; int value = 0; @@ -1234,10 +1245,7 @@ bool QQmlEnumTypeResolver::tryQualifiedEnumAssignment(const QmlIR::Object *obj, if (!ok) return true; - binding->type = QV4::CompiledData::Binding::Type_Number; - binding->value.d = (double)value; - binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum; - return true; + return assignEnumToBinding(binding, enumValue, value, isQtObject); } int QQmlEnumTypeResolver::evaluateEnum(const QString &scope, const QByteArray &enumValue, bool *ok) const @@ -2749,7 +2757,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR: bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) { - if (*_nameOfFunctionCalled == QStringLiteral("qsTr")) { + if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { QString translation; QV4::CompiledData::TranslationData translationData; translationData.number = -1; @@ -2791,7 +2799,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAnd binding->stringIndex = compiler->registerString(translation); binding->value.translationData = translationData; return true; - } else if (*_nameOfFunctionCalled == QStringLiteral("qsTrId")) { + } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { QString id; QV4::CompiledData::TranslationData translationData; translationData.number = -1; @@ -2825,7 +2833,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAnd binding->stringIndex = compiler->registerString(id); binding->value.translationData = translationData; return true; - } else if (*_nameOfFunctionCalled == QStringLiteral("QT_TR_NOOP") || *_nameOfFunctionCalled == QStringLiteral("QT_TRID_NOOP")) { + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { QVector<int>::ConstIterator param = _functionParameters.constBegin(); QVector<int>::ConstIterator end = _functionParameters.constEnd(); if (param == end) @@ -2842,7 +2850,7 @@ bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAnd binding->type = QV4::CompiledData::Binding::Type_String; binding->stringIndex = compiler->registerString(*stringParam->value); return true; - } else if (*_nameOfFunctionCalled == QStringLiteral("QT_TRANSLATE_NOOP")) { + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { QVector<int>::ConstIterator param = _functionParameters.constBegin(); QVector<int>::ConstIterator end = _functionParameters.constEnd(); if (param == end) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 3404350ece..240f591f91 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -196,6 +196,7 @@ public: bool resolveEnumBindings(); private: + bool assignEnumToBinding(QmlIR::Binding *binding, const QString &enumName, int enumValue, bool isQtObject); bool tryQualifiedEnumAssignment(const QmlIR::Object *obj, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *prop, QmlIR::Binding *binding); diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index eab5b7fd2d..d08d2aafa2 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -115,7 +115,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) if (strLit->literalToken.length < 2) continue; QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); - if (str == QStringLiteral("use strict")) { + if (str == QLatin1String("use strict")) { _env->isStrict = true; } else { // TODO: give a warning. @@ -148,7 +148,7 @@ void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocat void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *parameters) { while (parameters) { - if (parameters->name == QStringLiteral("arguments")) + if (parameters->name == QLatin1String("arguments")) _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; parameters = parameters->next; } @@ -170,7 +170,7 @@ bool Codegen::ScanFunctions::visit(CallExpression *ast) { if (! _env->hasDirectEval) { if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { - if (id->name == QStringLiteral("eval")) { + if (id->name == QLatin1String("eval")) { if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; _env->hasDirectEval = true; @@ -241,7 +241,7 @@ bool Codegen::ScanFunctions::visit(ExpressionStatement *ast) return false; } else { SourceLocation firstToken = ast->firstSourceLocation(); - if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QStringLiteral("function")) { + if (_sourceCode.midRef(firstToken.offset, firstToken.length) == QLatin1String("function")) { _cg->throwSyntaxError(firstToken, QStringLiteral("unexpected token")); } } @@ -1441,7 +1441,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) Q_ASSERT (index < e->members.size()); if (index != -1) { IR::ArgLocal *al = _block->LOCAL(index, scope); - if (name == QStringLiteral("arguments") || name == QStringLiteral("eval")) + if (name == QLatin1String("arguments") || name == QLatin1String("eval")) al->isArgumentsOrEval = true; return al; } diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 49e7d5c66e..0ae08160ab 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -95,7 +95,7 @@ void IRDecoder::visitMove(IR::Move *s) } } else if (s->target->asTemp() || s->target->asArgLocal()) { if (IR::Name *n = s->source->asName()) { - if (n->id && *n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. + if (n->id && *n->id == QLatin1String("this")) // TODO: `this' should be a builtin. loadThisObject(s->target); else if (n->builtin == IR::Name::builtin_qml_context) loadQmlContext(s->target); diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index c2b6fa477b..4c87b7557e 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -1145,7 +1145,7 @@ void IRPrinter::visitRegExp(RegExp *e) void IRPrinter::visitName(Name *e) { if (e->id) { - if (*e->id != QStringLiteral("this")) + if (*e->id != QLatin1String("this")) *out << '.'; *out << *e->id; } else { diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 0ff3b52870..7881ab951a 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -570,7 +570,7 @@ class DominatorTree bucket[s].push_back(n); link(p, n); if (bucket.contains(p)) { - foreach (BasicBlockIndex v, bucket[p]) { + for (BasicBlockIndex v : bucket[p]) { BasicBlockIndex y = ancestorWithLowestSemi(v, worklist); BasicBlockIndex semi_v = d->semi[v]; if (d->semi[y] == semi_v) @@ -670,7 +670,7 @@ public: foreach (BasicBlock *y, function->basicBlock(node)->out) if (idom[y->index()] != node) S.insert(y); - foreach (BasicBlockIndex child, np.children) { + for (BasicBlockIndex child : np.children) { const BasicBlockSet &ws = DF[child]; for (BasicBlockSet::const_iterator it = ws.begin(), eit = ws.end(); it != eit; ++it) { BasicBlock *w = *it; @@ -1733,7 +1733,7 @@ void convertToSSA(IR::Function *function, const DominatorTree &df, DefUses &defU W.reserve(8); // Place phi functions: - foreach (const Temp &a, variables.allTemps()) { + for (const Temp &a : variables.allTemps()) { if (a.isInvalid()) continue; if (!variables.isNonLocal(a)) @@ -1791,7 +1791,7 @@ void cleanupPhis(DefUses &defUses) std::vector<Phi *> allPhis; allPhis.reserve(32); - foreach (const Temp *def, defUses.defs()) { + for (const Temp *def : defUses.defs()) { Stmt *defStmt = defUses.defStmt(*def); if (!defStmt) continue; @@ -1808,7 +1808,7 @@ void cleanupPhis(DefUses &defUses) toRemove |= collectedPhis; } - foreach (Phi *phi, allPhis) { + for (Phi *phi : allPhis) { if (!toRemove.at(phi->id())) continue; @@ -2070,7 +2070,7 @@ protected: return; // TODO: maybe we can distinguish between built-ins of which we know that they do not have // a side-effect. - if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QStringLiteral("this"))) + if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QLatin1String("this"))) markAsSideEffect(); } diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp index f00d4b24f7..6edd28ea01 100644 --- a/src/qml/debugger/qqmldebugconnector.cpp +++ b/src/qml/debugger/qqmldebugconnector.cpp @@ -129,7 +129,7 @@ QQmlDebugConnector *QQmlDebugConnector::instance() if (!params->instance) { const QString serverConnector = QStringLiteral("QQmlDebugServer"); const QString nativeConnector = QStringLiteral("QQmlNativeDebugConnector"); - const bool isNative = params->arguments.startsWith(QStringLiteral("native")); + const bool isNative = params->arguments.startsWith(QLatin1String("native")); if (!params->pluginKey.isEmpty()) { if (params->pluginKey == serverConnector || params->pluginKey == nativeConnector) params->instance = loadQQmlDebugConnector(params->pluginKey); diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 4203cca8b3..6f738752a7 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -30,8 +30,6 @@ \relates QQmlEngine Equivalent to \c Q_DECLARE_METATYPE(TYPE *) and \c Q_DECLARE_METATYPE(QQmlListProperty<TYPE>) - - #include <QtQml> to use this macro. */ /*! @@ -44,8 +42,6 @@ Current the only supported type info is \c QML_HAS_ATTACHED_PROPERTIES which declares that the \a Type supports \l {Attached Properties and Attached Signal Handlers} {attached properties}. - - #include <QtQml> to use this macro. */ /*! @@ -58,8 +54,6 @@ Any existing QQmlEngines must be deleted before calling this function. This function only affects the application global cache. Delete the QQmlEngine to clear all cached data relating to that engine. - - #include <QtQml> to use this method. */ @@ -93,10 +87,6 @@ "com.mycompany.qmlcomponents": \code - #include <QtQml> - - ... - qmlRegisterType<MySliderItem>("com.mycompany.qmlcomponents", 1, 0, "Slider"); \endcode @@ -151,8 +141,6 @@ Returns the QML type id. - #include <QtQml> to use this function. - \sa qmlRegisterTypeNotAvailable() */ @@ -167,8 +155,6 @@ Returns the QML type id. - #include <QtQml> to use this function. - \sa qmlRegisterType(), {Registering Extension Objects} */ @@ -190,8 +176,6 @@ Returns the QML type id. - #include <QtQml> to use this function. - \sa qmlRegisterUncreatableType() */ @@ -208,8 +192,6 @@ the \a parser provided. Returns the QML type id. - - #include <QtQml> to use this function. */ /*! @@ -244,8 +226,6 @@ Without this, a generic "Game is not a type" message would be given. - #include <QtQml> to use this function. - \sa qmlRegisterUncreatableType() */ @@ -258,8 +238,6 @@ system. Instances of this type cannot be created from the QML system. - #include <QtQml> to use this function. - Returns the QML type id. */ @@ -270,8 +248,6 @@ This template function registers the C++ type in the QML system under the name \a typeName. - #include <QtQml> to use this function. - Returns the QML type id. */ @@ -305,10 +281,7 @@ } // Second, register the singleton type provider with QML by calling this function in an initialization function. - #include <QtQml> - ... qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider); - ... \endcode In order to use the registered singleton type in QML, you must import the singleton type. @@ -408,10 +381,7 @@ } // Third, register the singleton type provider with QML by calling this function in an initialization function. - #include <QtQml> - ... qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider); - ... \endcode In order to use the registered singleton type in QML, you must import the singleton type. @@ -481,10 +451,7 @@ \code // Second, register the QML singleton type by calling this function in an initialization function. - #include <QtQml> - ... qmlRegisterSingletonType(QUrl("file:///absolute/path/SingletonType.qml"), "Qt.example.qobjectSingleton", 1, 0, "RegisteredSingleton"); - ... \endcode In order to use the registered singleton type in QML, you must import the singleton type. @@ -514,8 +481,6 @@ Normally QML files can be loaded as types directly from other QML files, or using a qmldir file. This function allows registration of files to types from C++ code, such as when the type mapping needs to be procedurally determined at startup. - #include <QtQml> to use this function. - Returns -1 if the registration was not successful. */ @@ -538,8 +503,6 @@ uri, major version combination will lead to a runtime error. Call this after you have registered all of your types with the engine. - #include <QtQml> to use this function. - Returns true if the module with \a uri as a \l{Identified Modules} {module identifier} and \a majVersion as a major version number was found and locked, otherwise returns false. The module must contain exported types diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index c0b74c4fc6..04d769e4dc 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -448,8 +448,8 @@ right-hand-side of the property declaration must be a valid alias reference: [default] property alias <name>: <alias reference> \endcode -Unlike an ordinary property, an alias can only refer to a object, or the -property of a object, that is within the scope of the \l{QML Object Types} +Unlike an ordinary property, an alias can only refer to an object, or the +property of an object, that is within the scope of the \l{QML Object Types} {type} within which the alias is declared. It cannot contain arbitrary JavaScript expressions and it cannot refer to objects declared outside of the scope of its type. Also note the \e {alias reference} is not optional, diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index f66e3e0875..a6e085a913 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -1398,7 +1398,7 @@ LifeTimeInterval *RegisterAllocator::cloneFixedInterval(int reg, bool isFP, cons void RegisterAllocator::prepareRanges() { LifeTimeInterval ltiWithCalls = createFixedInterval(int(_info->calls().size())); - foreach (int callPosition, _info->calls()) + for (int callPosition : _info->calls()) ltiWithCalls.addRange(callPosition, callPosition); const int regCount = _normalRegisters.size(); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index c28df7d74a..317eb7a8a7 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1250,7 +1250,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return ld->d()->locale; if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); - if (const QV4::ArrayBuffer *d = value.as<ArrayBuffer>()) + if (const ArrayBuffer *d = value.as<ArrayBuffer>()) return d->asByteArray(); // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! @@ -1378,6 +1378,8 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(*reinterpret_cast<const double*>(ptr)); case QMetaType::QString: return newString(*reinterpret_cast<const QString*>(ptr))->asReturnedValue(); + case QMetaType::QByteArray: + return newArrayBuffer(*reinterpret_cast<const QByteArray*>(ptr))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast<const float*>(ptr)); case QMetaType::Short: @@ -1554,6 +1556,8 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return QV4::Encode(*reinterpret_cast<const double*>(data)); case QMetaType::QString: return newString(*reinterpret_cast<const QString*>(data))->asReturnedValue(); + case QMetaType::QByteArray: + return newArrayBuffer(*reinterpret_cast<const QByteArray*>(data))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast<const float*>(data)); case QMetaType::Short: @@ -1646,6 +1650,12 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) else *reinterpret_cast<QString*>(data) = value->toQString(); return true; + case QMetaType::QByteArray: + if (const ArrayBuffer *ab = value->as<ArrayBuffer>()) + *reinterpret_cast<QByteArray*>(data) = ab->asByteArray(); + else + *reinterpret_cast<QByteArray*>(data) = QByteArray(); + return true; case QMetaType::Float: *reinterpret_cast<float*>(data) = value->toNumber(); return true; diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 29f83da522..b3462fe9b1 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -41,8 +41,10 @@ #include "qv4scopedvalue_p.h" #include <QtQml/qjsengine.h> +#ifndef QT_NO_NETWORK #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> +#endif #include <QtCore/qfile.h> #include <QtQml/qqmlfile.h> @@ -57,7 +59,10 @@ QT_BEGIN_NAMESPACE QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &callback) - : v4(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0) + : v4(engine), m_url(url) +#ifndef QT_NO_NETWORK + , m_redirectCount(0), m_network(0) , m_reply(0) +#endif { if (qmlContext) m_qmlContext.set(engine, *qmlContext); @@ -66,6 +71,7 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, m_resultObject.set(v4, resultValue(v4)); +#ifndef QT_NO_NETWORK m_network = engine->v8Engine->networkAccessManager(); QNetworkRequest request; @@ -73,11 +79,17 @@ QV4Include::QV4Include(const QUrl &url, QV4::ExecutionEngine *engine, m_reply = m_network->get(request); QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +#else + finished(); +#endif } QV4Include::~QV4Include() { - delete m_reply; m_reply = 0; +#ifndef QT_NO_NETWORK + delete m_reply; + m_reply = 0; +#endif } QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status) @@ -123,6 +135,7 @@ QV4::ReturnedValue QV4Include::result() #define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 void QV4Include::finished() { +#ifndef QT_NO_NETWORK m_redirectCount++; if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { @@ -166,6 +179,12 @@ void QV4Include::finished() } else { resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError))); } +#else + QV4::Scope scope(v4); + QV4::ScopedObject resultObj(scope, m_resultObject.value()); + QV4::ScopedString status(scope, v4->newString(QStringLiteral("status"))); + resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError))); +#endif //QT_NO_NETWORK QV4::ScopedValue cb(scope, m_callbackFunction.value()); callback(cb, resultObj); @@ -188,14 +207,15 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) if (!context || !context->isJSContext) V4THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); - QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); - if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) - url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); - QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); if (ctx->argc() >= 2 && ctx->args()[1].as<QV4::FunctionObject>()) callbackFunction = ctx->args()[1]; +#ifndef QT_NO_NETWORK + QUrl url(scope.engine->resolvedUrl(ctx->args()[0].toQStringNoThrow())); + if (scope.engine->qmlEngine() && scope.engine->qmlEngine()->urlInterceptor()) + url = scope.engine->qmlEngine()->urlInterceptor()->intercept(url, QQmlAbstractUrlInterceptor::JavaScriptFile); + QString localFile = QQmlFile::urlToLocalFileOrQrc(url); QV4::ScopedValue result(scope); @@ -243,6 +263,12 @@ QV4::ReturnedValue QV4Include::method_include(QV4::CallContext *ctx) } return result->asReturnedValue(); +#else + QV4::ScopedValue result(scope); + result = resultValue(scope.engine, NetworkError); + callback(callbackFunction, result); + return result->asReturnedValue(); +#endif } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 257dc05e65..1750e6a7e1 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -62,7 +62,9 @@ QT_BEGIN_NAMESPACE class QQmlEngine; +#ifndef QT_NO_NETWORK class QNetworkAccessManager; +#endif class QNetworkReply; class QV4Include : public QObject { @@ -90,15 +92,16 @@ private: static void callback(const QV4::Value &callback, const QV4::Value &status); QV4::ExecutionEngine *v4; - QNetworkAccessManager *m_network; - QPointer<QNetworkReply> m_reply; - QUrl m_url; + +#ifndef QT_NO_NETWORK int m_redirectCount; + QNetworkAccessManager *m_network; + QPointer<QNetworkReply> m_reply; +#endif QV4::PersistentValue m_callbackFunction; QV4::PersistentValue m_resultObject; - QV4::PersistentValue m_qmlContext; }; diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index f264ec028e..8c5e7d8be1 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -51,8 +51,6 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(MathObject); -static const double qt_PI = 2.0 * ::asin(1.0); - Heap::MathObject::MathObject() { Scope scope(internalClass->engine); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8aae4dff1e..596a97a444 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -54,6 +54,7 @@ #include <private/qqmlbuiltinfunctions_p.h> #include <private/qv8engine_p.h> +#include <private/qv4arraybuffer_p.h> #include <private/qv4functionobject_p.h> #include <private/qv4runtime_p.h> #include <private/qv4variantobject_p.h> @@ -336,6 +337,8 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } else if (property->isV4Function()) { Scope scope(engine); ScopedContext global(scope, engine->qmlContext()); + if (!global) + global = engine->rootContext(); return QV4::QObjectMethod::create(global, object, property->coreIndex); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); @@ -1099,6 +1102,7 @@ private: // Pointers to allocData union { QString *qstringPtr; + QByteArray *qbyteArrayPtr; QVariant *qvariantPtr; QList<QObject *> *qlistPtr; QJSValue *qjsValuePtr; @@ -1220,6 +1224,13 @@ static int MatchScore(const QV4::Value &actual, int conversionType) default: return 10; } + } else if (actual.as<ArrayBuffer>()) { + switch (conversionType) { + case QMetaType::QByteArray: + return 0; + default: + return 10; + } } else if (actual.as<ArrayObject>()) { switch (conversionType) { case QMetaType::QJsonArray: @@ -1476,6 +1487,8 @@ void CallArgument::cleanup() { if (type == QMetaType::QString) { qstringPtr->~QString(); + } else if (type == QMetaType::QByteArray) { + qbyteArrayPtr->~QByteArray(); } else if (type == -1 || type == QMetaType::QVariant) { qvariantPtr->~QVariant(); } else if (type == qMetaTypeId<QJSValue>()) { @@ -1578,6 +1591,12 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q else qstringPtr = new (&allocData) QString(value.toQStringNoThrow()); type = callType; + } else if (callType == QMetaType::QByteArray) { + if (const ArrayBuffer *ab = value.as<ArrayBuffer>()) + qbyteArrayPtr = new (&allocData) QByteArray(ab->asByteArray()); + else + qbyteArrayPtr = new (&allocData) QByteArray(); + type = callType; } else if (callType == QMetaType::QObjectStar) { qobjectPtr = 0; if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>()) @@ -1675,6 +1694,8 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) return QV4::Encode(floatValue); } else if (type == QMetaType::QString) { return QV4::Encode(engine->newString(*qstringPtr)); + } else if (type == QMetaType::QByteArray) { + return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr)); } else if (type == QMetaType::QObjectStar) { QObject *object = qobjectPtr; if (object) diff --git a/src/qml/qml.pro b/src/qml/qml.pro index d75262bf0b..b119aa12af 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -1,5 +1,11 @@ TARGET = QtQml -QT = core-private network +QT = core-private + +no_network { + DEFINES += QT_NO_NETWORK +} else { + QT += network +} DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES @@ -11,9 +17,6 @@ solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 # Ensure this gcc optimization is switched off for mips platforms to avoid trouble with JIT. gcc:isEqual(QT_ARCH, "mips"): QMAKE_CXXFLAGS += -fno-reorder-blocks -MODULE_PLUGIN_TYPES = \ - qmltooling - exists("qqml_enable_gcov") { QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors LIBS_PRIVATE += -lgcov @@ -29,8 +32,6 @@ greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)| \ if(equals(QT_APPLE_CLANG_MAJOR_VERSION, 5):greaterThan(QT_APPLE_CLANG_MINOR_VERSION, 0)): \ WERROR += -Wno-error=unused-const-variable -load(qt_module) - HEADERS += qtqmlglobal.h \ qtqmlglobal_p.h @@ -46,3 +47,7 @@ include(qml/qml.pri) include(debugger/debugger.pri) include(animations/animations.pri) include(types/types.pri) + +MODULE_PLUGIN_TYPES = \ + qmltooling +load(qt_module) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index b8bd0f7f1d..fbd2d13e40 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -378,6 +378,13 @@ QQmlComponent::~QQmlComponent() if (d->state.completePending) { qWarning("QQmlComponent: Component destroyed while completion pending"); + + if (isError()) { + qWarning() << "This may have been caused by one of the following errors:"; + foreach (const QQmlError &error, d->state.errors) + qWarning().nospace().noquote() << QLatin1String(" ") << error; + } + d->completeCreate(); } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 0e99e4ca89..a5c920d28a 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -53,7 +53,6 @@ #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlcomponent_p.h" -#include "qqmlnetworkaccessmanagerfactory.h" #include "qqmldirparser_p.h" #include "qqmlextensioninterface.h" #include "qqmllist_p.h" @@ -63,25 +62,25 @@ #include "qqmlincubator.h" #include "qqmlabstracturlinterceptor.h" #include <private/qqmlboundsignal_p.h> - #include <QtCore/qstandardpaths.h> #include <QtCore/qsettings.h> - #include <QtCore/qmetaobject.h> -#include <QNetworkAccessManager> #include <QDebug> #include <QtCore/qcoreapplication.h> #include <QtCore/qdir.h> #include <QtCore/qmutex.h> #include <QtCore/qthread.h> #include <private/qthread_p.h> + +#ifndef QT_NO_NETWORK +#include "qqmlnetworkaccessmanagerfactory.h" +#include <QNetworkAccessManager> #include <QtNetwork/qnetworkconfigmanager.h> +#endif #include <private/qobject_p.h> #include <private/qmetaobject_p.h> - #include <private/qqmllocale_p.h> - #include <private/qqmlbind_p.h> #include <private/qqmlconnections_p.h> #include <private/qqmltimer_p.h> @@ -493,6 +492,10 @@ The following functions are also on the Qt object. from right to left. \endlist \row + \li \c application.font + \li This read-only property holds the default application font as + returned by \l QGuiApplication::font(). + \row \li \c application.arguments \li This is a string list of the arguments the executable was invoked with. \row @@ -531,6 +534,7 @@ The following functions are also on the Qt object. \li application.active \li application.state \li application.layoutDirection + \li application.font \endlist */ @@ -605,9 +609,11 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), activeObjectCreator(0), - networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), - scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), - incubatorCount(0), incubationController(0) +#ifndef QT_NO_NETWORK + networkAccessManager(0), networkAccessManagerFactory(0), +#endif + urlInterceptor(0), scarceResourcesRefCount(0), importDatabase(e), typeLoader(e), + uniqueId(1), incubatorCount(0), incubationController(0) { } @@ -1066,7 +1072,17 @@ QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const return d->urlInterceptor; } +void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) +{ + if (activeObjectCreator) { + activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index)); + } else { + void *args[] = { 0 }; + QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); + } +} +#ifndef QT_NO_NETWORK /*! Sets the \a factory to use for creating QNetworkAccessManager(s). @@ -1095,16 +1111,6 @@ QQmlNetworkAccessManagerFactory *QQmlEngine::networkAccessManagerFactory() const return d->networkAccessManagerFactory; } -void QQmlEnginePrivate::registerFinalizeCallback(QObject *obj, int index) -{ - if (activeObjectCreator) { - activeObjectCreator->finalizeCallbacks()->append(qMakePair(QPointer<QObject>(obj), index)); - } else { - void *args[] = { 0 }; - QMetaObject::metacall(obj, QMetaObject::InvokeMetaMethod, index, args); - } -} - QNetworkAccessManager *QQmlEnginePrivate::createNetworkAccessManager(QObject *parent) const { QMutexLocker locker(&networkAccessManagerMutex); @@ -1143,6 +1149,7 @@ QNetworkAccessManager *QQmlEngine::networkAccessManager() const Q_D(const QQmlEngine); return d->getNetworkAccessManager(); } +#endif // QT_NO_NETWORK /*! diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index eee0587531..28bd66047e 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -44,6 +44,7 @@ #include <QtCore/qobject.h> #include <QtCore/qmap.h> #include <QtQml/qjsengine.h> +#include <QtQml/qqml.h> #include <QtQml/qqmlerror.h> #include <QtQml/qqmldebug.h> @@ -86,8 +87,10 @@ class QQmlExpression; class QQmlContext; class QQmlType; class QUrl; +#ifndef QT_NO_NETWORK class QNetworkAccessManager; class QQmlNetworkAccessManagerFactory; +#endif class QQmlIncubationController; class Q_QML_EXPORT QQmlEngine : public QJSEngine { @@ -114,10 +117,12 @@ public: bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors); +#ifndef QT_NO_NETWORK void setNetworkAccessManagerFactory(QQmlNetworkAccessManagerFactory *); QQmlNetworkAccessManagerFactory *networkAccessManagerFactory() const; QNetworkAccessManager *networkAccessManager() const; +#endif void setUrlInterceptor(QQmlAbstractUrlInterceptor* urlInterceptor); QQmlAbstractUrlInterceptor* urlInterceptor() const; diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index e56a3fc57f..7258ffe6ed 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -158,12 +158,12 @@ public: void registerFinalizeCallback(QObject *obj, int index); QQmlObjectCreator *activeObjectCreator; - +#ifndef QT_NO_NETWORK QNetworkAccessManager *createNetworkAccessManager(QObject *parent) const; QNetworkAccessManager *getNetworkAccessManager() const; mutable QNetworkAccessManager *networkAccessManager; mutable QQmlNetworkAccessManagerFactory *networkAccessManagerFactory; - +#endif QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders; QQmlAbstractUrlInterceptor* urlInterceptor; @@ -172,9 +172,8 @@ public: void referenceScarceResources(); void dereferenceScarceResources(); - QQmlTypeLoader typeLoader; QQmlImportDatabase importDatabase; - + QQmlTypeLoader typeLoader; QString offlineStoragePath; diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index ea4e9a1013..8ce0cc2026 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -67,6 +67,8 @@ static char assets_string[] = "assets"; #endif class QQmlFilePrivate; + +#ifndef QT_NO_NETWORK class QQmlFileNetworkReply : public QObject { Q_OBJECT @@ -97,6 +99,7 @@ private: int m_redirectCount; QNetworkReply *m_reply; }; +#endif class QQmlFilePrivate { @@ -114,10 +117,12 @@ public: Error error; QString errorString; - +#ifndef QT_NO_NETWORK QQmlFileNetworkReply *reply; +#endif }; +#ifndef QT_NO_NETWORK int QQmlFileNetworkReply::finishedIndex = -1; int QQmlFileNetworkReply::downloadProgressIndex = -1; int QQmlFileNetworkReply::networkFinishedIndex = -1; @@ -200,9 +205,13 @@ void QQmlFileNetworkReply::networkDownloadProgress(qint64 a, qint64 b) { emit downloadProgress(a, b); } +#endif // QT_NO_NETWORK QQmlFilePrivate::QQmlFilePrivate() -: error(None), reply(0) +: error(None) +#ifndef QT_NO_NETWORK +, reply(0) +#endif { } @@ -225,7 +234,9 @@ QQmlFile::QQmlFile(QQmlEngine *e, const QString &url) QQmlFile::~QQmlFile() { +#ifndef QT_NO_NETWORK delete d->reply; +#endif delete d; d = 0; } @@ -263,8 +274,10 @@ QQmlFile::Status QQmlFile::status() const { if (d->url.isEmpty() && d->urlString.isEmpty()) return Null; +#ifndef QT_NO_NETWORK else if (d->reply) return Loading; +#endif else if (d->error != QQmlFilePrivate::None) return Error; else @@ -321,7 +334,11 @@ void QQmlFile::load(QQmlEngine *engine, const QUrl &url) d->error = QQmlFilePrivate::NotFound; } } else { +#ifndef QT_NO_NETWORK d->reply = new QQmlFileNetworkReply(engine, d, url); +#else + d->error = QQmlFilePrivate::NotFound; +#endif } } @@ -348,10 +365,14 @@ void QQmlFile::load(QQmlEngine *engine, const QString &url) d->error = QQmlFilePrivate::NotFound; } } else { +#ifndef QT_NO_NETWORK QUrl qurl(url); d->url = qurl; d->urlString = QString(); d->reply = new QQmlFileNetworkReply(engine, d, qurl); +#else + d->error = QQmlFilePrivate::NotFound; +#endif } } @@ -368,6 +389,7 @@ void QQmlFile::clear(QObject *) clear(); } +#ifndef QT_NO_NETWORK bool QQmlFile::connectFinished(QObject *object, const char *method) { if (!d || !d->reply) { @@ -411,6 +433,7 @@ bool QQmlFile::connectDownloadProgress(QObject *object, int method) return QMetaObject::connect(d->reply, QQmlFileNetworkReply::downloadProgressIndex, object, method); } +#endif /*! Returns true if QQmlFile will open \a url synchronously. diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h index b0910cc0f4..3dd683a2cd 100644 --- a/src/qml/qml/qqmlfile.h +++ b/src/qml/qml/qqmlfile.h @@ -80,10 +80,12 @@ public: void clear(); void clear(QObject *); +#ifndef QT_NO_NETWORK bool connectFinished(QObject *, const char *); bool connectFinished(QObject *, int); bool connectDownloadProgress(QObject *, const char *); bool connectDownloadProgress(QObject *, int); +#endif static bool isSynchronous(const QString &url); static bool isSynchronous(const QUrl &url); diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp index f9fea0279e..c94db8e168 100644 --- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp @@ -41,6 +41,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_NETWORK + /*! \class QQmlNetworkAccessManagerFactory \since 5.0 @@ -101,4 +103,6 @@ QQmlNetworkAccessManagerFactory::~QQmlNetworkAccessManagerFactory() implementation of this method is reentrant. */ +#endif //QT_NO_NETWORK + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h index 8e3b94fad3..ba3561b9f4 100644 --- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h @@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_NETWORK class QNetworkAccessManager; class Q_QML_EXPORT QQmlNetworkAccessManagerFactory @@ -55,6 +56,8 @@ public: }; +#endif //QT_NO_NETWORK + QT_END_NAMESPACE #endif // QQMLNETWORKACCESSMANAGERFACTORY_H diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b516cc7d89..1a7896bde8 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -640,7 +640,7 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip) if (_compiledObject->idIndex) { const QQmlPropertyData *idProperty = propertyData.last(); - Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QStringLiteral("id")); + Q_ASSERT(!idProperty || !idProperty->isValid() || idProperty->name(_qobject) == QLatin1String("id")); if (idProperty && idProperty->isValid() && idProperty->isWritable() && idProperty->propType == QMetaType::QString) { QV4::CompiledData::Binding idBinding; idBinding.propertyNameIndex = 0; // Not used diff --git a/src/qml/qml/qqmlscriptstring.cpp b/src/qml/qml/qqmlscriptstring.cpp index b027f043ec..3230e69b6d 100644 --- a/src/qml/qml/qqmlscriptstring.cpp +++ b/src/qml/qml/qqmlscriptstring.cpp @@ -135,10 +135,10 @@ bool QQmlScriptString::operator==(const QQmlScriptString &other) const if (d->isStringLiteral || other.d->isStringLiteral) return d->isStringLiteral && other.d->isStringLiteral && d->script == other.d->script; - if (d->script == QStringLiteral("true") || - d->script == QStringLiteral("false") || - d->script == QStringLiteral("undefined") || - d->script == QStringLiteral("null")) + if (d->script == QLatin1String("true") || + d->script == QLatin1String("false") || + d->script == QLatin1String("undefined") || + d->script == QLatin1String("null")) return d->script == other.d->script; return d->context == other.d->context && @@ -172,7 +172,7 @@ Returns whether the content of the QQmlScriptString is the \c undefined literal. */ bool QQmlScriptString::isUndefinedLiteral() const { - return d->script == QStringLiteral("undefined"); + return d->script == QLatin1String("undefined"); } /*! @@ -180,7 +180,7 @@ Returns whether the content of the QQmlScriptString is the \c null literal. */ bool QQmlScriptString::isNullLiteral() const { - return d->script == QStringLiteral("null"); + return d->script == QLatin1String("null"); } /*! @@ -211,8 +211,8 @@ sets \a ok to true. Otherwise returns false and sets \a ok to false. */ bool QQmlScriptString::booleanLiteral(bool *ok) const { - bool isTrue = d->script == QStringLiteral("true"); - bool isFalse = !isTrue && d->script == QStringLiteral("false"); + bool isTrue = d->script == QLatin1String("true"); + bool isFalse = !isTrue && d->script == QLatin1String("false"); if (ok) *ok = isTrue || isFalse; return isTrue ? true : false; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 52a7562b58..15e8d62efc 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -112,6 +112,7 @@ namespace { }; } +#ifndef QT_NO_NETWORK // This is a lame object that we need to ensure that slots connected to // QNetworkReply get called in the correct thread (the loader thread). // As QQmlTypeLoader lives in the main thread, and we can't use @@ -131,6 +132,7 @@ public slots: private: QQmlTypeLoader *l; }; +#endif // QT_NO_NETWORK class QQmlTypeLoaderThread : public QQmlThread { @@ -138,9 +140,10 @@ class QQmlTypeLoaderThread : public QQmlThread public: QQmlTypeLoaderThread(QQmlTypeLoader *loader); +#ifndef QT_NO_NETWORK QNetworkAccessManager *networkAccessManager() const; QQmlTypeLoaderNetworkReplyProxy *networkReplyProxy() const; - +#endif // QT_NO_NETWORK void load(QQmlDataBlob *b); void loadAsync(QQmlDataBlob *b); void loadWithStaticData(QQmlDataBlob *b, const QByteArray &); @@ -163,11 +166,13 @@ private: void initializeEngineMain(QQmlExtensionInterface *iface, const char *uri); QQmlTypeLoader *m_loader; +#ifndef QT_NO_NETWORK mutable QNetworkAccessManager *m_networkAccessManager; mutable QQmlTypeLoaderNetworkReplyProxy *m_networkReplyProxy; +#endif // QT_NO_NETWORK }; - +#ifndef QT_NO_NETWORK QQmlTypeLoaderNetworkReplyProxy::QQmlTypeLoaderNetworkReplyProxy(QQmlTypeLoader *l) : l(l) { @@ -196,7 +201,7 @@ void QQmlTypeLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) l->networkReplyProgress(reply, replySize, replySize); l->networkReplyFinished(reply); } - +#endif // QT_NO_NETWORK /*! \class QQmlDataBlob @@ -480,6 +485,7 @@ void QQmlDataBlob::done() { } +#ifndef QT_NO_NETWORK /*! Invoked if there is a network error while fetching this blob. @@ -532,6 +538,7 @@ void QQmlDataBlob::networkError(QNetworkReply::NetworkError networkError) setError(error); } +#endif // QT_NO_NETWORK /*! Called if \a blob, which was previously waited for, has an error. @@ -730,12 +737,16 @@ void QQmlDataBlob::ThreadData::setProgress(quint8 v) } QQmlTypeLoaderThread::QQmlTypeLoaderThread(QQmlTypeLoader *loader) -: m_loader(loader), m_networkAccessManager(0), m_networkReplyProxy(0) +: m_loader(loader) +#ifndef QT_NO_NETWORK +, m_networkAccessManager(0), m_networkReplyProxy(0) +#endif // QT_NO_NETWORK { // Do that after initializing all the members. startup(); } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QQmlTypeLoaderThread::networkAccessManager() const { Q_ASSERT(isThisThread()); @@ -753,6 +764,7 @@ QQmlTypeLoaderNetworkReplyProxy *QQmlTypeLoaderThread::networkReplyProxy() const Q_ASSERT(m_networkReplyProxy); // Must call networkAccessManager() first return m_networkReplyProxy; } +#endif // QT_NO_NETWORK void QQmlTypeLoaderThread::load(QQmlDataBlob *b) { @@ -810,10 +822,12 @@ void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface, void QQmlTypeLoaderThread::shutdownThread() { +#ifndef QT_NO_NETWORK delete m_networkAccessManager; m_networkAccessManager = 0; delete m_networkReplyProxy; m_networkReplyProxy = 0; +#endif // QT_NO_NETWORK } void QQmlTypeLoaderThread::loadThread(QQmlDataBlob *b) @@ -899,12 +913,14 @@ void QQmlTypeLoader::invalidate() m_thread = 0; } +#ifndef QT_NO_NETWORK // Need to delete the network replies after // the loader thread is shutdown as it could be // getting new replies while we clear them for (NetworkReplies::Iterator iter = m_networkReplies.begin(); iter != m_networkReplies.end(); ++iter) (*iter)->release(); m_networkReplies.clear(); +#endif // QT_NO_NETWORK } void QQmlTypeLoader::lock() @@ -1082,7 +1098,7 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) setData(blob, &file); } else { - +#ifndef QT_NO_NETWORK QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url)); QQmlTypeLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy(); blob->addref(); @@ -1099,13 +1115,14 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob) #ifdef DATABLOB_DEBUG qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString())); -#endif - +#endif // DATABLOB_DEBUG +#endif // QT_NO_NETWORK } } #define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16 +#ifndef QT_NO_NETWORK void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply) { Q_ASSERT(m_thread->isThisThread()); @@ -1161,6 +1178,7 @@ void QQmlTypeLoader::networkReplyProgress(QNetworkReply *reply, m_thread->callDownloadProgressChanged(blob, blob->m_data.progress()); } } +#endif // QT_NO_NETWORK /*! Return the QQmlEngine associated with this loader diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 01d223bbce..49a4ac716a 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -53,7 +53,9 @@ #include <QtCore/qobject.h> #include <QtCore/qatomic.h> +#ifndef QT_NO_NETWORK #include <QtNetwork/qnetworkreply.h> +#endif #include <QtQml/qqmlerror.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlfile.h> @@ -156,7 +158,9 @@ protected: virtual void dataReceived(const Data &) = 0; virtual void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit*) = 0; virtual void done(); +#ifndef QT_NO_NETWORK virtual void networkError(QNetworkReply::NetworkError); +#endif virtual void dependencyError(QQmlDataBlob *); virtual void dependencyComplete(QQmlDataBlob *); virtual void allDependenciesDone(); @@ -320,17 +324,21 @@ public: private: friend class QQmlDataBlob; friend class QQmlTypeLoaderThread; +#ifndef QT_NO_NETWORK friend class QQmlTypeLoaderNetworkReplyProxy; +#endif // QT_NO_NETWORK void shutdownThread(); void loadThread(QQmlDataBlob *); void loadWithStaticDataThread(QQmlDataBlob *, const QByteArray &); void loadWithCachedUnitThread(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit); +#ifndef QT_NO_NETWORK void networkReplyFinished(QNetworkReply *); void networkReplyProgress(QNetworkReply *, qint64, qint64); typedef QHash<QNetworkReply *, QQmlDataBlob *> NetworkReplies; +#endif void setData(QQmlDataBlob *, const QByteArray &); void setData(QQmlDataBlob *, QQmlFile *); @@ -362,7 +370,9 @@ private: QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; +#ifndef QT_NO_NETWORK NetworkReplies m_networkReplies; +#endif TypeCache m_typeCache; ScriptCache m_scriptCache; QmldirCache m_qmldirCache; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 2f2c6cf7dc..33fe655368 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -125,6 +125,28 @@ ReturnedValue QmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, Q return w.asReturnedValue(); } +static int enumForSingleton(String *name, QObject *qobjectSingleton) +{ + // ### Optimize + QByteArray enumName = name->toQString().toUtf8(); + const QMetaObject *metaObject = qobjectSingleton->metaObject(); + for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + bool ok; + int value = e.keyToValue(enumName.constData(), &ok); + if (ok) + return value; + } + return -1; +} + +static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *name, QQmlType *type) +{ + const QString message = + QStringLiteral("Cannot access enum value '%1' of '%2', enum values need to start with an uppercase letter.") + .arg(name->toQString()).arg(QLatin1String(type->typeName())); + return v4->throwTypeError(message); +} ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasProperty) { @@ -141,9 +163,9 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope QQmlContextData *context = v4->callingQmlContext(); QObject *object = w->d()->object; + QQmlType *type = w->d()->type; - if (w->d()->type) { - QQmlType *type = w->d()->type; + if (type) { // singleton types are handled differently to other types. if (type->isSingleton()) { @@ -153,24 +175,29 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope QObject *qobjectSingleton = siinfo->qobjectApi(e); if (qobjectSingleton) { + // check for enum value - if (name->startsWithUpper()) { - if (w->d()->mode == Heap::QmlTypeWrapper::IncludeEnums) { - // ### Optimize - QByteArray enumName = name->toQString().toUtf8(); - const QMetaObject *metaObject = qobjectSingleton->metaObject(); - for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { - QMetaEnum e = metaObject->enumerator(ii); - bool ok; - int value = e.keyToValue(enumName.constData(), &ok); - if (ok) - return QV4::Primitive::fromInt32(value).asReturnedValue(); - } - } + const bool includeEnums = w->d()->mode == Heap::QmlTypeWrapper::IncludeEnums; + if (includeEnums && name->startsWithUpper()) { + const int value = enumForSingleton(name, qobjectSingleton); + if (value != -1) + return QV4::Primitive::fromInt32(value).asReturnedValue(); } // check for property. - return QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); + bool ok; + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + if (hasProperty) + *hasProperty = ok; + + // Warn when attempting to access a lowercased enum value, singleton case + if (!ok && includeEnums && !name->startsWithUpper()) { + const int value = enumForSingleton(name, qobjectSingleton); + if (value != -1) + return throwLowercaseEnumError(v4, name, type); + } + + return result; } else if (!siinfo->scriptApi(e).isUndefined()) { // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); @@ -227,9 +254,20 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope Q_ASSERT(!"Unreachable"); } + bool ok = false; + const ReturnedValue result = Object::get(m, name, &ok); if (hasProperty) - *hasProperty = false; - return Object::get(m, name, hasProperty); + *hasProperty = ok; + + // Warn when attempting to access a lowercased enum value, non-singleton case + if (!ok && type && !type->isSingleton() && !name->startsWithUpper()) { + bool enumOk = false; + type->enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &enumOk); + if (enumOk) + return throwLowercaseEnumError(v4, name, type); + } + + return result; } diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index fe02c6633e..af94ec757c 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -70,7 +70,7 @@ using namespace QV4; -#ifndef QT_NO_XMLSTREAMREADER +#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_NETWORK) #define V4THROW_REFERENCE(string) { \ ScopedObject error(scope, ctx->engine()->newReferenceErrorObject(QStringLiteral(string))); \ @@ -2040,6 +2040,6 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4) QT_END_NAMESPACE -#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMREADER && QT_NO_NETWORK #include <qqmlxmlhttprequest.moc> diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h index 7bbfb5243c..df30873915 100644 --- a/src/qml/qml/qqmlxmlhttprequest_p.h +++ b/src/qml/qml/qqmlxmlhttprequest_p.h @@ -55,7 +55,7 @@ #include <QtCore/qglobal.h> #include <private/qqmlglobal_p.h> -#ifndef QT_NO_XMLSTREAMREADER +#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_NETWORK) QT_BEGIN_NAMESPACE @@ -64,7 +64,7 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *); QT_END_NAMESPACE -#endif // QT_NO_XMLSTREAMREADER +#endif // QT_NO_XMLSTREAMREADER && QT_NO_NETWORK #endif // QQMLXMLHTTPREQUEST_P_H diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index d322088f61..89f128a0e7 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -159,18 +159,22 @@ QV8Engine::~QV8Engine() delete m_extensionData[ii]; m_extensionData.clear(); +#if !defined(QT_NO_XMLSTREAMREADER) && defined(QT_NO_NETWORK) qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); m_xmlHttpRequestData = 0; +#endif delete m_listModelData; m_listModelData = 0; delete m_v4Engine; } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QV8Engine::networkAccessManager() { return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); } +#endif const QSet<QString> &QV8Engine::illegalNames() const { @@ -189,8 +193,10 @@ void QV8Engine::initializeGlobal() QQmlDateExtension::registerExtension(m_v4Engine); QQmlNumberExtension::registerExtension(m_v4Engine); +#if !defined(QT_NO_XMLSTREAMREADER) && !defined(QT_NO_NETWORK) qt_add_domexceptions(m_v4Engine); m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine); +#endif qt_add_sqlexceptions(m_v4Engine); diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 4ffd36ca34..9054c731db 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -194,10 +194,12 @@ public: void freezeObject(const QV4::Value &value); +#ifndef QT_NO_NETWORK // Return the network access manager for this engine. By default this returns the network // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 // instance (like in WorkerScript). virtual QNetworkAccessManager *networkAccessManager(); +#endif // Return the list of illegal id names (the names of the properties on the global object) const QSet<QString> &illegalNames() const; diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index e2063dee7d..ed9a8533c0 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -75,7 +75,7 @@ public: void QQmlBindPrivate::validate(QObject *binding) const { - if (!obj) + if (!obj || (when.isValid() && !when)) return; if (!prop.isValid()) { @@ -185,6 +185,8 @@ void QQmlBind::setWhen(bool v) return; d->when = v; + if (v && d->componentComplete) + d->validate(this); eval(); } diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index e819e4ee7d..bc15b2fd9b 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -52,10 +52,12 @@ #include <QtCore/qwaitcondition.h> #include <QtCore/qfile.h> #include <QtCore/qdatetime.h> -#include <QtNetwork/qnetworkaccessmanager.h> #include <QtQml/qqmlinfo.h> #include <QtQml/qqmlfile.h> +#ifndef QT_NO_NETWORK +#include <QtNetwork/qnetworkaccessmanager.h> #include "qqmlnetworkaccessmanagerfactory.h" +#endif #include <private/qv8engine_p.h> #include <private/qv4serialize_p.h> @@ -141,7 +143,10 @@ public: ~WorkerEngine(); void init(); + +#ifndef QT_NO_NETWORK virtual QNetworkAccessManager *networkAccessManager(); +#endif QQuickWorkerScriptEnginePrivate *p; @@ -150,7 +155,9 @@ public: QV4::PersistentValue onmessage; private: QV4::PersistentValue createsend; +#ifndef QT_NO_NETWORK QNetworkAccessManager *accessManager; +#endif }; WorkerEngine *workerEngine; @@ -194,14 +201,19 @@ private: }; QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) -: QV8Engine(0), p(parent), accessManager(0) +: QV8Engine(0), p(parent) +#ifndef QT_NO_NETWORK +, accessManager(0) +#endif { m_v4Engine->v8Engine = this; } QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() { +#ifndef QT_NO_NETWORK delete accessManager; +#endif } void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() @@ -262,6 +274,7 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i return v->asReturnedValue(); } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() { if (!accessManager) { @@ -273,6 +286,7 @@ QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAcc } return accessManager; } +#endif QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) : workerEngine(0), qmlengine(engine), m_nextId(0) diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index 0e32dc51e2..acd5c9729b 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -13,10 +13,10 @@ intel_icc: WERROR += -ww2415 clang:if(greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 3)): \ WERROR += -Wno-error=unused-const-variable -load(qt_module) - include(../3rdparty/masm/masm-defs.pri) include(../qml/parser/parser.pri) include(../qml/jsruntime/jsruntime.pri) include(../qml/compiler/compiler.pri) include(../qml/memory/memory.pri) + +load(qt_module) diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index 289a0584e0..42224e9751 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -13,8 +13,6 @@ qtHaveModule(widgets) { DEFINES += QT_QMLTEST_WITH_WIDGETS } -load(qt_module) - # Install qmltestcase.prf into the Qt mkspecs so that "CONFIG += qmltestcase" # can be used in customer applications to build against QtQuickTest. feature.path = $$[QT_INSTALL_DATA]/mkspecs/features @@ -33,3 +31,5 @@ HEADERS += \ $$PWD/qtestoptions_p.h DEFINES += QT_QML_DEBUG_NO_WARNING + +load(qt_module) diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index adee4330d3..9ba93a1241 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -296,7 +296,7 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD const QFileInfo testPathInfo(testPath); if (testPathInfo.isFile()) { - if (!testPath.endsWith(QStringLiteral(".qml"))) { + if (!testPath.endsWith(QLatin1String(".qml"))) { qWarning("'%s' does not have the suffix '.qml'.", qPrintable(testPath)); return 1; } diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp index 278e62d4f6..544ca04754 100644 --- a/src/quick/designer/qquickdesignersupportitems.cpp +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -175,19 +175,19 @@ static QQmlType *getQmlType(const QString &typeName, int majorNumber, int minorN static bool isCrashingType(QQmlType *type) { if (type) { - if (type->qmlTypeName() == QStringLiteral("QtMultimedia/MediaPlayer")) + if (type->qmlTypeName() == QLatin1String("QtMultimedia/MediaPlayer")) return true; - if (type->qmlTypeName() == QStringLiteral("QtMultimedia/Audio")) + if (type->qmlTypeName() == QLatin1String("QtMultimedia/Audio")) return true; - if (type->qmlTypeName() == QStringLiteral("QtQuick.Controls/MenuItem")) + if (type->qmlTypeName() == QLatin1String("QtQuick.Controls/MenuItem")) return true; - if (type->qmlTypeName() == QStringLiteral("QtQuick.Controls/Menu")) + if (type->qmlTypeName() == QLatin1String("QtQuick.Controls/Menu")) return true; - if (type->qmlTypeName() == QStringLiteral("QtQuick/Timer")) + if (type->qmlTypeName() == QLatin1String("QtQuick/Timer")) return true; } diff --git a/src/quick/doc/images/columnlayout.png b/src/quick/doc/images/columnlayout.png Binary files differnew file mode 100644 index 0000000000..f03eb7b996 --- /dev/null +++ b/src/quick/doc/images/columnlayout.png diff --git a/src/quick/doc/images/gridlayout.png b/src/quick/doc/images/gridlayout.png Binary files differnew file mode 100644 index 0000000000..493813c481 --- /dev/null +++ b/src/quick/doc/images/gridlayout.png diff --git a/src/quick/doc/images/qtquicklayouts-example-layouts.png b/src/quick/doc/images/qtquicklayouts-example-layouts.png Binary files differnew file mode 100644 index 0000000000..94619bae3f --- /dev/null +++ b/src/quick/doc/images/qtquicklayouts-example-layouts.png diff --git a/src/quick/doc/images/rowlayout-minimum.png b/src/quick/doc/images/rowlayout-minimum.png Binary files differnew file mode 100644 index 0000000000..5875325c54 --- /dev/null +++ b/src/quick/doc/images/rowlayout-minimum.png diff --git a/src/quick/doc/images/rowlayout.png b/src/quick/doc/images/rowlayout.png Binary files differnew file mode 100644 index 0000000000..519a62fddd --- /dev/null +++ b/src/quick/doc/images/rowlayout.png diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf index 044c1696ff..4f141a733a 100644 --- a/src/quick/doc/qtquick.qdocconf +++ b/src/quick/doc/qtquick.qdocconf @@ -33,7 +33,7 @@ qhp.QtQuick.subprojects.examples.selectors = fake:example tagfile = ../../../doc/qtquick/qtquick.tags -depends += qtcore qtxmlpatterns qtqml qtgui qtlinguist qtquickcontrols qtquicklayouts qtdoc qtquickdialogs qtsensors qtwidgets qmake qtmultimedia qtgraphicaleffects +depends += qtcore qtxmlpatterns qtqml qtgui qtlinguist qtquickcontrols qtdoc qtquickdialogs qtsensors qtwidgets qmake qtmultimedia qtgraphicaleffects headerdirs += ..\ ../../quickwidgets diff --git a/src/quick/doc/snippets/qml/windowconstraints.qml b/src/quick/doc/snippets/qml/windowconstraints.qml new file mode 100644 index 0000000000..3f3534f494 --- /dev/null +++ b/src/quick/doc/snippets/qml/windowconstraints.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** 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. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.2 + +Window { + //! [binddefaultsize] + width: layout.implicitWidth + height: layout.implicitHeight + //! [binddefaultsize] + //! [bindconstraints] + minimumWidth: layout.Layout.minimumWidth + minimumHeight: layout.Layout.minimumHeight + maximumWidth: 1000 + maximumHeight: layout.Layout.maximumHeight + //! [bindconstraints] + + //! [rowlayout] + //! [anchoring] + RowLayout { + id: layout + anchors.fill: parent + //! [anchoring] + spacing: 6 + Rectangle { + color: 'azure' + Layout.fillWidth: true + Layout.minimumWidth: 50 + Layout.preferredWidth: 100 + Layout.maximumWidth: 300 + Layout.minimumHeight: 150 + Text { + anchors.centerIn: parent + text: parent.width + 'x' + parent.height + } + } + Rectangle { + color: 'plum' + Layout.fillWidth: true + Layout.minimumWidth: 100 + Layout.preferredWidth: 200 + Layout.preferredHeight: 100 + Text { + anchors.centerIn: parent + text: parent.width + 'x' + parent.height + } + } + } + //! [rowlayout] +} diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc new file mode 100644 index 0000000000..0be66fad2c --- /dev/null +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** 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. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtquicklayouts-index.html + \title Qt Quick Layouts + \brief A module with a set of QML elements that arrange QML items in a user interface. + + Qt Quick Layouts are a set of QML types used to arrange items in a user interface. In contrast + to \l{Item Positioners}{positioners}, Qt Quick Layouts can also resize their items. This makes + them well suited for resizable user interfaces. Since layouts are items they can consequently + be nested. + + The module is new in Qt 5.1 and requires \l{Qt Quick} 2.1. + + Visit the \l{Qt Quick Layouts Overview} page to get started. + + \section1 Layouts + + \annotatedlist layouts + + \section1 Related information + + \list + \li \l{Qt Quick} + \li \l{Qt Quick Layouts Overview} + \li \l{Qt Quick Layouts - Basic Example} + \li \l{Qt Quick Layouts QML Types}{Qt Quick Layouts QML Types} + \endlist +*/ diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc new file mode 100644 index 0000000000..1b6e7dc539 --- /dev/null +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** 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. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtquicklayouts-overview.html + \title Qt Quick Layouts Overview + \brief A set of APIs for arranging QML items in a user interface. + + Qt Quick Layouts are items that are used to arrange items in a user interface. Since Qt Quick + Layouts also resize their items, they are well suited for resizable user interfaces. + + \section1 Getting started + + The QML types can be imported into your application using the following import statement in your \c {.qml} file. + + \code + import QtQuick.Layouts 1.2 + \endcode + + \section1 Key Features + + + Some of the key features are: + + \list + \li \l{Layout::alignment}{Alignment} of items can be specified with the + \l{Layout::alignment}{Layout.alignment} property + \li \l{Layout::fillWidth}{Resizable items} can be specified with the + \l{Layout::fillWidth}{Layout.fillWidth} and \l{Layout::fillHeight}{Layout.fillHeight} + properties. + \li \l{Size constraints} can be specified with + \l{Layout::minimumWidth}{Layout.minimumWidth}, + \l{Layout::preferredWidth}{Layout.preferredWidth}, and + \l{Layout::maximumWidth}{Layout.maximumWidth} properties ("Width" can be replaced + with "Height" for specifying similar constraints to the height). + \li \l{RowLayout::spacing}{Spacings} can be specified with \l{RowLayout::spacing}{spacing}, + \l{GridLayout::rowSpacing}{rowSpacing} or \l{GridLayout::columnSpacing}{columnSpacing} + \endlist + + In addition to the above features, GridLayout adds these features: + \list + \li \l{Layout::row}{Grid coordinates} can be specified with the \l{Layout::row}{Layout.row} and + \l{Layout::column}{Layout.column}. + \li \l{GridLayout::flow}{Automatic grid coordinates} used together with the + \l{GridLayout::flow}{flow}, \l{GridLayout::rows}{rows}, and + \l{GridLayout::columns}{columns} properties. + \li \l{Layout::columnSpan}{Spans} across rows or columns can be specified with the + \l{Layout::rowSpan}{Layout.rowSpan} and \l{Layout::columnSpan}{Layout.columnSpan} + properties. + \endlist + + + + \section1 Size Constraints + Since an item can be resized by its layout, the layout needs to know the + \l{Layout::minimumWidth}{minimum}, \l{Layout::preferredWidth}{preferred}, + and \l{Layout::maximumWidth}{maximum} sizes of all items where \l{Layout::fillWidth}{Layout.fillWidth} or + \l{Layout::fillHeight}{Layout.fillHeight} is set to \c true. + For instance, the following will produce a layout with two rectangles lying side-by-side that + stretches horizontally. The azure rectangle can be resized from 50x150 to 300x150, and the plum + rectangle can be resized from 100x100 to ∞x100. + + \snippet windowconstraints.qml rowlayout + + \image rowlayout-minimum.png "RowLayout at its minimum" + + Combining each item's constraints will give these implicit constraints to the layout element: + + \table + \header + \li + \li minimum + \li preferred + \li maximum + \row + \li implicit constraints (width) + \li 156 + \li 306 + \li ∞ (\c Number.POSITIVE_INFINITY) + \row + \li implicit constraints (heights) + \li 150 + \li 150 + \li 150 + \endtable + + Thus, the layout cannot be narrower than 156 or be taller or shorter than 150 without breaking + any of the constraints of its child items. + + \section2 Specifying Preferred Size + For each item, the effective preferred size may come from one of several candidate properties. + For determining the effective preferred size, it will query these candidate properties in the + following order, and use the first candidate with a valid width or height. + + \table + \header + \li Candidate properties + \li Description + \row + \li \l{Layout::preferredWidth}{Layout.preferredWidth} or + \l{Layout::preferredHeight}{Layout.preferredHeight} + \li These properties are supposed to be modified by the application if the default implicit + size does not give the optimal arrangement. + \row + \li \l{Item::implicitWidth}{implicitWidth} or \l{Item::implicitHeight}{implicitHeight} + \li These properties are supposed to be supplied by each item to give a meaningful ideal size, + for example the size needed to display all the contents of a \l Text type. + An implicit width or height of \c 0 is interpreted as invalid. + \row + \li \l{Item::width}{width} and \l{Item::height}{height} + \li If none of the above properties are valid, the layout will resort to the + \l{Item::width}{width} and \l{Item::height}{height} properties. + \endtable + + An item can specify \l{Layout::preferredWidth}{Layout.preferredWidth} without having to specify + \l{Layout::preferredHeight}{Layout.preferredHeight}. In this case, the effective preferred + height will be determined from the \l{Item::implicitHeight}{implicitHeight} (or ultimately + \l{Item::height}{height}). + + \note Resorting to \l{Item::width}{width} or \l{Item::height}{height} properties is only + provided as a final fallback. If you want to override the preferred size, it is recommended to + use \l{Layout::preferredWidth}{Layout.preferredWidth} or + \l{Layout::preferredHeight}{Layout.preferredHeight}. Relying on the \l{Item::width}{width} or + \l{Item::height}{height} properties for specifying the preferred size might give some + unexpected behavior. For instance, changing the \l{Item::width}{width} or + \l{Item::height}{height} properties won't trigger a layout rearrangement. Also, when the layout + is forced to do a full rebuild it might use the actual width and height, and not the width and + height specified in the QML file. + + + \section1 Connecting windows and layouts + You can just use normal anchoring concepts to ensure that the layout will follow the window + resizing. + + \snippet qml/windowconstraints.qml anchoring + + The size constraints of layouts can be used to ensure that the window cannot be resized beyond + the layout constraints. You can take the size constraints from the layout and set these + constraints on the minimumWidth, minimumHeight, maximumWidth, and maximumHeight of the Window + element. The following code ensures that the window cannot be resized beyond the constraints of + the layout: + + \snippet qml/windowconstraints.qml bindconstraints + + \note Since layout.Layout.maximumWidth is infinite in this case, we cannot bind that to the + maximumWidth property of Window, since that is an integer number. We therefore set a fixed + maximum width to 1000. + + Finally, you usually want the initial size of the window to be the layout's implicit size: + + \snippet qml/windowconstraints.qml binddefaultsize +*/ diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc new file mode 100644 index 0000000000..8f390c83db --- /dev/null +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** 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. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlmodule QtQuick.Layouts 1.3 + \title Qt Quick Layouts QML Types + \ingroup qmlmodules + \brief Provides QML types for arranging QML items in a user interface. + + The \l{Qt Quick Layouts} module provides QML types for arranging + QML items in a user interface. + These QML types work in conjunction with \l{Qt Quick} and + \l{Qt Quick Controls}. + + The QML types can be imported into your application using the + following import statement in your .qml file. + + \code + import QtQuick.Layouts 1.3 + \endcode + +*/ diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc index dffcabbd5b..cb281a2d4a 100644 --- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc +++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc @@ -102,7 +102,7 @@ has changed. If the QList changes, it is necessary to reset the model by calling QQmlContext::setContextProperty() again. -\section2 QAbstractItemModel +\section2 QAbstractItemModel subclass A model can be defined by subclassing QAbstractItemModel. This is the best approach if you have a more complex model that cannot be supported diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc index 98a77a48d8..4bdd02241d 100644 --- a/src/quick/doc/src/qtquick.qdoc +++ b/src/quick/doc/src/qtquick.qdoc @@ -101,6 +101,7 @@ Additional Qt Quick information: containing a JavaScript interface for an SQLite database \li \l{Qt Quick Particles QML Types}{Particles} - provides a particle system for Qt Quick + \li \l{Qt Quick Layouts}{Layouts} - provides layouts for arranging Qt Quick items \li \l{Qt Quick Window QML Types}{Window} - contains types for creating top-level windows and accessing screen information \li \l{Qt Quick Dialogs}{Dialogs} - contains types for creating and diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 2d991bb670..bacdfad557 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -223,13 +223,13 @@ static bool qSetFontSizeFromToken(QFont &font, const QString &fontSizeToken) const QString value = trimmedToken.left(trimmedToken.size() - 2); bool ok = false; int size = 0; - if (unitStr == QStringLiteral("px")) { + if (unitStr == QLatin1String("px")) { size = qParseFontSizeFromToken(value, ok); if (ok) { font.setPixelSize(size); return true; } - } else if (unitStr == QStringLiteral("pt")) { + } else if (unitStr == QLatin1String("pt")) { size = qParseFontSizeFromToken(value, ok); if (ok) { font.setPointSize(size); @@ -368,9 +368,9 @@ static QFont qt_font_from_string(const QString& fontString, const QFont ¤t // We know that font-size must be specified and it must be before font-family // (which could potentially have "px" or "pt" in its name), so extract it now. - int fontSizeEnd = fontString.indexOf(QStringLiteral("px")); + int fontSizeEnd = fontString.indexOf(QLatin1String("px")); if (fontSizeEnd == -1) - fontSizeEnd = fontString.indexOf(QStringLiteral("pt")); + fontSizeEnd = fontString.indexOf(QLatin1String("pt")); if (fontSizeEnd == -1) { qWarning().nospace() << "Context2D: Invalid font size unit in font string."; return currentFont; @@ -769,53 +769,53 @@ void qt_image_boxblur(QImage& image, int radius, bool quality) static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator) { - if (compositeOperator == QStringLiteral("source-over")) { + if (compositeOperator == QLatin1String("source-over")) { return QPainter::CompositionMode_SourceOver; - } else if (compositeOperator == QStringLiteral("source-out")) { + } else if (compositeOperator == QLatin1String("source-out")) { return QPainter::CompositionMode_SourceOut; - } else if (compositeOperator == QStringLiteral("source-in")) { + } else if (compositeOperator == QLatin1String("source-in")) { return QPainter::CompositionMode_SourceIn; - } else if (compositeOperator == QStringLiteral("source-atop")) { + } else if (compositeOperator == QLatin1String("source-atop")) { return QPainter::CompositionMode_SourceAtop; - } else if (compositeOperator == QStringLiteral("destination-atop")) { + } else if (compositeOperator == QLatin1String("destination-atop")) { return QPainter::CompositionMode_DestinationAtop; - } else if (compositeOperator == QStringLiteral("destination-in")) { + } else if (compositeOperator == QLatin1String("destination-in")) { return QPainter::CompositionMode_DestinationIn; - } else if (compositeOperator == QStringLiteral("destination-out")) { + } else if (compositeOperator == QLatin1String("destination-out")) { return QPainter::CompositionMode_DestinationOut; - } else if (compositeOperator == QStringLiteral("destination-over")) { + } else if (compositeOperator == QLatin1String("destination-over")) { return QPainter::CompositionMode_DestinationOver; - } else if (compositeOperator == QStringLiteral("lighter")) { + } else if (compositeOperator == QLatin1String("lighter")) { return QPainter::CompositionMode_Lighten; - } else if (compositeOperator == QStringLiteral("copy")) { + } else if (compositeOperator == QLatin1String("copy")) { return QPainter::CompositionMode_Source; - } else if (compositeOperator == QStringLiteral("xor")) { + } else if (compositeOperator == QLatin1String("xor")) { return QPainter::CompositionMode_Xor; - } else if (compositeOperator == QStringLiteral("qt-clear")) { + } else if (compositeOperator == QLatin1String("qt-clear")) { return QPainter::CompositionMode_Clear; - } else if (compositeOperator == QStringLiteral("qt-destination")) { + } else if (compositeOperator == QLatin1String("qt-destination")) { return QPainter::CompositionMode_Destination; - } else if (compositeOperator == QStringLiteral("qt-multiply")) { + } else if (compositeOperator == QLatin1String("qt-multiply")) { return QPainter::CompositionMode_Multiply; - } else if (compositeOperator == QStringLiteral("qt-screen")) { + } else if (compositeOperator == QLatin1String("qt-screen")) { return QPainter::CompositionMode_Screen; - } else if (compositeOperator == QStringLiteral("qt-overlay")) { + } else if (compositeOperator == QLatin1String("qt-overlay")) { return QPainter::CompositionMode_Overlay; - } else if (compositeOperator == QStringLiteral("qt-darken")) { + } else if (compositeOperator == QLatin1String("qt-darken")) { return QPainter::CompositionMode_Darken; - } else if (compositeOperator == QStringLiteral("qt-lighten")) { + } else if (compositeOperator == QLatin1String("qt-lighten")) { return QPainter::CompositionMode_Lighten; - } else if (compositeOperator == QStringLiteral("qt-color-dodge")) { + } else if (compositeOperator == QLatin1String("qt-color-dodge")) { return QPainter::CompositionMode_ColorDodge; - } else if (compositeOperator == QStringLiteral("qt-color-burn")) { + } else if (compositeOperator == QLatin1String("qt-color-burn")) { return QPainter::CompositionMode_ColorBurn; - } else if (compositeOperator == QStringLiteral("qt-hard-light")) { + } else if (compositeOperator == QLatin1String("qt-hard-light")) { return QPainter::CompositionMode_HardLight; - } else if (compositeOperator == QStringLiteral("qt-soft-light")) { + } else if (compositeOperator == QLatin1String("qt-soft-light")) { return QPainter::CompositionMode_SoftLight; - } else if (compositeOperator == QStringLiteral("qt-difference")) { + } else if (compositeOperator == QLatin1String("qt-difference")) { return QPainter::CompositionMode_Difference; - } else if (compositeOperator == QStringLiteral("qt-exclusion")) { + } else if (compositeOperator == QLatin1String("qt-exclusion")) { return QPainter::CompositionMode_Exclusion; } return QPainter::CompositionMode_SourceOver; @@ -1314,7 +1314,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_globalCompositeOperation(QV4::C QString mode = ctx->args()[0].toQString(); QPainter::CompositionMode cm = qt_composite_mode_from_string(mode); - if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over")) + if (cm == QPainter::CompositionMode_SourceOver && mode != QLatin1String("source-over")) return QV4::Encode::undefined(); if (cm != r->d()->context->state.globalCompositeOperation) { @@ -1431,10 +1431,10 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillRule(QV4::CallContext *ctx) QV4::ScopedValue value(scope, ctx->argument(0)); - if ((value->isString() && value->toQString() == QStringLiteral("WindingFill")) + if ((value->isString() && value->toQString() == QLatin1String("WindingFill")) || (value->isInt32() && value->integerValue() == Qt::WindingFill)) { r->d()->context->state.fillRule = Qt::WindingFill; - } else if ((value->isString() && value->toQStringNoThrow() == QStringLiteral("OddEvenFill")) + } else if ((value->isString() && value->toQStringNoThrow() == QLatin1String("OddEvenFill")) || (value->isInt32() && value->integerValue() == Qt::OddEvenFill)) { r->d()->context->state.fillRule = Qt::OddEvenFill; } else { @@ -1730,16 +1730,16 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(QV4::CallCon pattern->d()->brush.setTextureImage(patternTexture); QString repetition = ctx->args()[1].toQStringNoThrow(); - if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) { + if (repetition == QLatin1String("repeat") || repetition.isEmpty()) { pattern->d()->patternRepeatX = true; pattern->d()->patternRepeatY = true; - } else if (repetition == QStringLiteral("repeat-x")) { + } else if (repetition == QLatin1String("repeat-x")) { pattern->d()->patternRepeatX = true; pattern->d()->patternRepeatY = false; - } else if (repetition == QStringLiteral("repeat-y")) { + } else if (repetition == QLatin1String("repeat-y")) { pattern->d()->patternRepeatX = false; pattern->d()->patternRepeatY = true; - } else if (repetition == QStringLiteral("no-repeat")) { + } else if (repetition == QLatin1String("no-repeat")) { pattern->d()->patternRepeatX = false; pattern->d()->patternRepeatY = false; } else { @@ -1793,11 +1793,11 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_lineCap(QV4::CallContext *ctx) QString lineCap = ctx->args()[0].toQString(); Qt::PenCapStyle cap; - if (lineCap == QStringLiteral("round")) + if (lineCap == QLatin1String("round")) cap = Qt::RoundCap; - else if (lineCap == QStringLiteral("butt")) + else if (lineCap == QLatin1String("butt")) cap = Qt::FlatCap; - else if (lineCap == QStringLiteral("square")) + else if (lineCap == QLatin1String("square")) cap = Qt::SquareCap; else return QV4::Encode::undefined(); @@ -1852,11 +1852,11 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_lineJoin(QV4::CallContext *ctx) QString lineJoin = ctx->args()[0].toQString(); Qt::PenJoinStyle join; - if (lineJoin == QStringLiteral("round")) + if (lineJoin == QLatin1String("round")) join = Qt::RoundJoin; - else if (lineJoin == QStringLiteral("bevel")) + else if (lineJoin == QLatin1String("bevel")) join = Qt::BevelJoin; - else if (lineJoin == QStringLiteral("miter")) + else if (lineJoin == QLatin1String("miter")) join = Qt::SvgMiterJoin; else return QV4::Encode::undefined(); @@ -2733,15 +2733,15 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(QV4::CallContext * QString textBaseline = s->toQString(); QQuickContext2D::TextBaseLineType tb; - if (textBaseline == QStringLiteral("alphabetic")) + if (textBaseline == QLatin1String("alphabetic")) tb = QQuickContext2D::Alphabetic; - else if (textBaseline == QStringLiteral("hanging")) + else if (textBaseline == QLatin1String("hanging")) tb = QQuickContext2D::Hanging; - else if (textBaseline == QStringLiteral("top")) + else if (textBaseline == QLatin1String("top")) tb = QQuickContext2D::Top; - else if (textBaseline == QStringLiteral("bottom")) + else if (textBaseline == QLatin1String("bottom")) tb = QQuickContext2D::Bottom; - else if (textBaseline == QStringLiteral("middle")) + else if (textBaseline == QLatin1String("middle")) tb = QQuickContext2D::Middle; else return QV4::Encode::undefined(); diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 12bd95101f..f3513f447a 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -605,6 +605,11 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() void QQuickContext2DFBOTexture::endPainting() { QQuickContext2DTexture::endPainting(); + + // There may not be an FBO due to zero width or height. + if (!m_fbo) + return; + if (m_multisampledFbo) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index 3ae2512c98..42033b6135 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -47,8 +47,10 @@ #include <QtQml/qqmlfile.h> #include <QtQml/qqmlengine.h> #include <QtGui/qmovie.h> +#ifndef QT_NO_NETWORK #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> +#endif QT_BEGIN_NAMESPACE @@ -144,8 +146,10 @@ QQuickAnimatedImage::QQuickAnimatedImage(QQuickItem *parent) QQuickAnimatedImage::~QQuickAnimatedImage() { Q_D(QQuickAnimatedImage); +#ifndef QT_NO_NETWORK if (d->reply) d->reply->deleteLater(); +#endif delete d->_movie; qDeleteAll(d->frameMap); d->frameMap.clear(); @@ -264,10 +268,12 @@ void QQuickAnimatedImage::setSource(const QUrl &url) if (url == d->url) return; +#ifndef QT_NO_NETWORK if (d->reply) { d->reply->deleteLater(); d->reply = 0; } +#endif d->setImage(QImage()); qDeleteAll(d->frameMap); @@ -318,6 +324,7 @@ void QQuickAnimatedImage::load() d->_movie = new QMovie(lf); movieRequestFinished(); } else { +#ifndef QT_NO_NETWORK if (d->status != Loading) { d->status = Loading; emit statusChanged(d->status); @@ -334,6 +341,7 @@ void QQuickAnimatedImage::load() this, SLOT(movieRequestFinished())); QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(requestProgress(qint64,qint64))); +#endif } } } @@ -342,8 +350,10 @@ void QQuickAnimatedImage::load() void QQuickAnimatedImage::movieRequestFinished() { + Q_D(QQuickAnimatedImage); +#ifndef QT_NO_NETWORK if (d->reply) { d->redirectCount++; if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) { @@ -359,6 +369,7 @@ void QQuickAnimatedImage::movieRequestFinished() d->redirectCount=0; d->_movie = new QMovie(d->reply); } +#endif if (!d->_movie->isValid()) { qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString(); diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h index 9474254252..9b284f966d 100644 --- a/src/quick/items/qquickanimatedimage_p_p.h +++ b/src/quick/items/qquickanimatedimage_p_p.h @@ -58,7 +58,9 @@ QT_BEGIN_NAMESPACE class QMovie; +#ifndef QT_NO_NETWORK class QNetworkReply; +#endif class QQuickAnimatedImagePrivate : public QQuickImagePrivate { @@ -66,7 +68,10 @@ class QQuickAnimatedImagePrivate : public QQuickImagePrivate public: QQuickAnimatedImagePrivate() - : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0), oldPlaying(false) + : playing(true), paused(false), preset_currentframe(0), _movie(0), oldPlaying(false) +#ifndef QT_NO_NETWORK + , reply(0), redirectCount(0) +#endif { } @@ -76,9 +81,11 @@ public: bool paused; int preset_currentframe; QMovie *_movie; + bool oldPlaying; +#ifndef QT_NO_NETWORK QNetworkReply *reply; int redirectCount; - bool oldPlaying; +#endif QMap<int, QQuickPixmap *> frameMap; }; diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index b3c1e8fb40..f5df5763bb 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -544,6 +544,7 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode() m_node->setGeometry(g); m_node->setMaterial(m_material); m_node->setFlag(QSGGeometryNode::OwnsMaterial); + m_node->setFlag(QSGGeometryNode::OwnsGeometry); sizeVertices(); return m_node; } diff --git a/src/quick/items/qquickborderimage.cpp b/src/quick/items/qquickborderimage.cpp index 66f414d816..5f8dd7b989 100644 --- a/src/quick/items/qquickborderimage.cpp +++ b/src/quick/items/qquickborderimage.cpp @@ -43,7 +43,9 @@ #include <QtQml/qqmlinfo.h> #include <QtQml/qqmlfile.h> #include <QtQml/qqmlengine.h> +#ifndef QT_NO_NETWORK #include <QtNetwork/qnetworkreply.h> +#endif #include <QtCore/qfile.h> #include <QtCore/qmath.h> #include <QtGui/qguiapplication.h> @@ -169,9 +171,11 @@ QQuickBorderImage::QQuickBorderImage(QQuickItem *parent) QQuickBorderImage::~QQuickBorderImage() { +#ifndef QT_NO_NETWORK Q_D(QQuickBorderImage); if (d->sciReply) d->sciReply->deleteLater(); +#endif } /*! @@ -270,10 +274,12 @@ void QQuickBorderImage::setSource(const QUrl &url) if (url == d->url) return; +#ifndef QT_NO_NETWORK if (d->sciReply) { d->sciReply->deleteLater(); d->sciReply = 0; } +#endif d->url = url; d->sciurl = QUrl(); @@ -311,6 +317,7 @@ void QQuickBorderImage::load() setGridScaledImage(QQuickGridScaledImage(&file)); return; } else { +#ifndef QT_NO_NETWORK if (d->progress != 0.0) { d->progress = 0.0; emit progressChanged(d->progress); @@ -320,6 +327,7 @@ void QQuickBorderImage::load() d->sciReply = qmlEngine(this)->networkAccessManager()->get(req); qmlobject_connect(d->sciReply, QNetworkReply, SIGNAL(finished()), this, QQuickBorderImage, SLOT(sciRequestFinished())) +#endif } } else { QQuickPixmap::Options options; @@ -529,6 +537,7 @@ void QQuickBorderImage::requestFinished() pixmapChange(); } +#ifndef QT_NO_NETWORK #define BORDERIMAGE_MAX_REDIRECT 16 void QQuickBorderImage::sciRequestFinished() @@ -558,6 +567,7 @@ void QQuickBorderImage::sciRequestFinished() setGridScaledImage(sci); } } +#endif // QT_NO_NETWORK void QQuickBorderImage::doUpdate() { diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h index 7f8b172a21..f2764660f6 100644 --- a/src/quick/items/qquickborderimage_p.h +++ b/src/quick/items/qquickborderimage_p.h @@ -101,7 +101,9 @@ private: private Q_SLOTS: void doUpdate(); void requestFinished() Q_DECL_OVERRIDE; +#ifndef QT_NO_NETWORK void sciRequestFinished(); +#endif private: Q_DISABLE_COPY(QQuickBorderImage) diff --git a/src/quick/items/qquickborderimage_p_p.h b/src/quick/items/qquickborderimage_p_p.h index 478de88a52..1dc530e34e 100644 --- a/src/quick/items/qquickborderimage_p_p.h +++ b/src/quick/items/qquickborderimage_p_p.h @@ -58,17 +58,20 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_NETWORK class QNetworkReply; +#endif class QQuickBorderImagePrivate : public QQuickImageBasePrivate { Q_DECLARE_PUBLIC(QQuickBorderImage) public: QQuickBorderImagePrivate() - : border(0), sciReply(0), - horizontalTileMode(QQuickBorderImage::Stretch), - verticalTileMode(QQuickBorderImage::Stretch), - redirectCount(0), pixmapChanged(false) + : border(0), horizontalTileMode(QQuickBorderImage::Stretch), + verticalTileMode(QQuickBorderImage::Stretch), pixmapChanged(false) +#ifndef QT_NO_NETWORK + , sciReply(0), redirectCount(0) +#endif { } @@ -90,12 +93,14 @@ public: QQuickScaleGrid *border; QUrl sciurl; - QNetworkReply *sciReply; QQuickBorderImage::TileMode horizontalTileMode; QQuickBorderImage::TileMode verticalTileMode; - int redirectCount; - bool pixmapChanged : 1; + +#ifndef QT_NO_NETWORK + QNetworkReply *sciReply; + int redirectCount; +#endif }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 3663b379bb..b0245f402b 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -65,6 +65,8 @@ static const int FlickThreshold = 15; // will ensure the Flickable retains the grab on consecutive flicks. static const int RetainGrabVelocity = 100; +static const int MovementEndingTimerInterval = 100; + static qreal EaseOvershoot(qreal t) { return qAtan(t); } @@ -244,6 +246,8 @@ void QQuickFlickablePrivate::init() contentItem->setParentItem(q); qmlobject_connect(&timeline, QQuickTimeLine, SIGNAL(completed()), q, QQuickFlickable, SLOT(timelineCompleted())) + qmlobject_connect(&velocityTimeline, QQuickTimeLine, SIGNAL(completed()), + q, QQuickFlickable, SLOT(velocityTimelineCompleted())) q->setAcceptedMouseButtons(Qt::LeftButton); q->setFiltersChildMouseEvents(true); QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem); @@ -1202,6 +1206,8 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp hData.velocity = 0; } + if (momentum && !hData.flicking && !vData.flicking) + flickingStarted(hData.velocity != 0, vData.velocity != 0); draggingStarting(); if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) @@ -1390,10 +1396,17 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->timer.start(); d->maybeBeginDrag(currentTimestamp, event->posF()); break; + case Qt::NoScrollPhase: // default phase with an ordinary wheel mouse case Qt::ScrollUpdate: + if (d->scrollingPhase) { + d->pressed = true; + d->movementEndingTimer.start(MovementEndingTimerInterval, this); + } break; case Qt::ScrollEnd: + d->pressed = false; d->scrollingPhase = false; + d->movementEndingTimer.start(MovementEndingTimerInterval, this); d->draggingEnding(); event->accept(); returnToBounds(); @@ -1415,7 +1428,6 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) valid = true; } if (valid) { - d->vData.flicking = false; d->flickY(d->vData.velocity); d->flickingStarted(false, true); if (d->vData.flicking) { @@ -1435,7 +1447,6 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) valid = true; } if (valid) { - d->hData.flicking = false; d->flickX(d->hData.velocity); d->flickingStarted(true, false); if (d->hData.flicking) { @@ -1548,6 +1559,12 @@ void QQuickFlickable::timerEvent(QTimerEvent *event) if (d->delayedPressEvent) { d->replayDelayedPress(); } + } else if (event->timerId() == d->movementEndingTimer.timerId()) { + d->movementEndingTimer.stop(); + d->pressed = false; + d->stealMouse = false; + if (!d->velocityTimeline.isActive()) + movementEnding(true, true); } } @@ -2475,6 +2492,22 @@ bool QQuickFlickable::isMovingVertically() const return d->vData.moving; } +void QQuickFlickable::velocityTimelineCompleted() +{ + Q_D(QQuickFlickable); + if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive()) + || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) { + return; + } + // With subclasses such as GridView, velocityTimeline.completed is emitted repeatedly: + // for example setting currentIndex results in a visual "flick" which the user + // didn't initiate directly. We don't want to end movement repeatedly, and in + // that case movementEnding will happen after the sequence of movements ends. + if (d->vData.flicking) + movementEnding(); + d->updateBeginningEnd(); +} + void QQuickFlickable::timelineCompleted() { Q_D(QQuickFlickable); diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index a7995c5025..6cf78dcf63 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -259,6 +259,7 @@ protected Q_SLOTS: void movementStarting(); void movementEnding(); void movementEnding(bool hMovementEnding, bool vMovementEnding); + void velocityTimelineCompleted(); void timelineCompleted(); protected: diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 83e38af8a9..3c59b19ec2 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -218,6 +218,7 @@ public: bool calcVelocity : 1; bool pixelAligned : 1; QElapsedTimer timer; + QBasicTimer movementEndingTimer; qint64 lastPosTime; qint64 lastPressTime; QPointF lastPos; diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 7da9801218..da48fd869d 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -182,6 +182,8 @@ public: bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) Q_DECL_OVERRIDE; bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) Q_DECL_OVERRIDE; + void removeItem(FxViewItem *item); + FxViewItem *newViewItem(int index, QQuickItem *item) Q_DECL_OVERRIDE; void initializeViewItem(FxViewItem *item) Q_DECL_OVERRIDE; void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer) Q_DECL_OVERRIDE; @@ -567,6 +569,17 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal return changed; } +void QQuickGridViewPrivate::removeItem(FxViewItem *item) +{ + if (item->transitionScheduledOrRunning()) { + qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName(); + item->releaseAfterTransition = true; + releasePendingTransition.append(item); + } else { + releaseItem(item); + } +} + bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) { FxGridItemSG *item = 0; @@ -581,13 +594,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer if (item->index != -1) visibleIndex++; visibleItems.removeFirst(); - if (item->transitionScheduledOrRunning()) { - qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName(); - item->releaseAfterTransition = true; - releasePendingTransition.append(item); - } else { - releaseItem(item); - } + removeItem(item); changed = true; } while (visibleItems.count() > 1 @@ -597,13 +604,7 @@ bool QQuickGridViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal buffer break; qCDebug(lcItemViewDelegateLifecycle) << "refill: remove last" << visibleIndex+visibleItems.count()-1; visibleItems.removeLast(); - if (item->transitionScheduledOrRunning()) { - qCDebug(lcItemViewDelegateLifecycle) << "\tnot releasing animating item:" << item->index << item->item->objectName(); - item->releaseAfterTransition = true; - releasePendingTransition.append(item); - } else { - releaseItem(item); - } + removeItem(item); changed = true; } @@ -1524,7 +1525,7 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight) existing applications. When explicitly set, it will cease to be bound to the interactive property. - \sa \l {Flickable::}{interactive} + \sa {Flickable::}{interactive} */ /*! @@ -2406,11 +2407,12 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch int i = count - 1; int from = tempPos - buffer - displayMarginBeginning; - while (i >= 0) { - if (rowPos > from && insertionIdx < visibleIndex) { - // item won't be visible, just note the size for repositioning - insertResult->countChangeBeforeVisible++; - } else { + if (rowPos > from && insertionIdx < visibleIndex) { + // items won't be visible, just note the size for repositioning + insertResult->countChangeBeforeVisible += count; + insertResult->sizeChangesBeforeVisiblePos += ((count + columns - 1) / columns) * rowSize(); + } else { + while (i >= 0) { // item is before first visible e.g. in cache buffer FxViewItem *item = 0; if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i)))) @@ -2426,19 +2428,40 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch insertResult->changedFirstItem = true; if (!change.isMove()) { addedItems->append(item); - item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + if (transitioner) + item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + else + item->moveTo(QPointF(colPos, rowPos), true); } insertResult->sizeChangesBeforeVisiblePos += rowSize(); + + if (--colNum < 0 ) { + colNum = columns - 1; + rowPos -= rowSize(); + } + colPos = colNum * colSize(); + index++; + i--; } + } - if (--colNum < 0 ) { - colNum = columns - 1; - rowPos -= rowSize(); + // There may be gaps in the index sequence of visibleItems because + // of the index shift/update done before the insertion just above. + // Find if there is any... + int firstOkIdx = -1; + for (int i = 0; i <= insertionIdx && i < visibleItems.count() - 1; i++) { + if (visibleItems.at(i)->index + 1 != visibleItems.at(i + 1)->index) { + firstOkIdx = i + 1; + break; } - colPos = colNum * colSize(); - index++; - i--; } + // ... and remove all the items before that one + for (int i = 0; i < firstOkIdx; i++) { + FxViewItem *nvItem = visibleItems.takeFirst(); + addedItems->removeOne(nvItem); + removeItem(nvItem); + } + } else { int i = 0; int to = buffer+displayMarginEnd+tempPos+size()-1; @@ -2463,7 +2486,10 @@ bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch movingIntoView->append(MovedItem(item, change.moveKey(item->index))); } else { addedItems->append(item); - item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + if (transitioner) + item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true); + else + item->moveTo(QPointF(colPos, rowPos), true); } insertResult->sizeChangesAfterVisiblePos += rowSize(); diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 866a83c42a..8117baa2fe 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -226,7 +226,7 @@ void QQuickImageBase::load() // will be used, as usual. bool setDevicePixelRatio = false; if (!d->sourcesize.isValid()) { - if (loadUrl.scheme() == QStringLiteral("image")) { + if (loadUrl.scheme() == QLatin1String("image")) { setDevicePixelRatio = true; } else { QString stringUrl = loadUrl.path(QUrl::PrettyDecoded); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 5be1c86fb4..b2c039660e 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2074,6 +2074,10 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) \value ItemRotationHasChanged The item's rotation has changed. ItemChangeData::realValue contains the new rotation. + + \value ItemDevicePixelRatioHasChanged The device pixel ratio of the screen + the item is on has changed. ItemChangedData::realValue contains the new + device pixel ratio. */ /*! @@ -2477,6 +2481,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward) { Q_ASSERT(item); + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward; if (!item->window()) return item; @@ -2487,19 +2492,25 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo bool all = QGuiApplication::styleHints()->tabFocusBehavior() == Qt::TabFocusAllControls; QQuickItem *from = 0; + bool isTabFence = item->d_func()->isTabFence; if (forward) { - from = item->parentItem(); + if (!isTabFence) + from = item->parentItem(); } else { if (!item->childItems().isEmpty()) from = item->d_func()->childItems.constFirst(); - else + else if (!isTabFence) from = item->parentItem(); } bool skip = false; QQuickItem * startItem = item; QQuickItem * firstFromItem = from; QQuickItem *current = item; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem; do { + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current; + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from; skip = false; QQuickItem *last = current; @@ -2513,7 +2524,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo else lastChild = prevTabChildItem(current, -1); } - bool isTabFence = current->d_func()->isTabFence; + isTabFence = current->d_func()->isTabFence; if (isTabFence && !hasChildren) return current; @@ -2568,9 +2579,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo return startItem; } } - if (!firstFromItem) { //start from root - startItem = current; - firstFromItem = from; + if (!firstFromItem) { + if (startItem->d_func()->isTabFence) { + if (current == startItem) + firstFromItem = from; + } else { //start from root + startItem = current; + firstFromItem = from; + } } } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible() || !(all || QQuickItemPrivate::canAcceptTabFocus(current))); @@ -4563,9 +4579,9 @@ QQuickItem *QQuickItem::childAt(qreal x, qreal y) const // Map coordinates to the child element's coordinate space QPointF point = mapToItem(child, QPointF(x, y)); if (child->isVisible() && point.x() >= 0 - && child->width() >= point.x() + && child->width() > point.x() && point.y() >= 0 - && child->height() >= point.y()) + && child->height() > point.y()) return child; } return 0; @@ -5968,6 +5984,8 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt } break; case QQuickItem::ItemAntialiasingHasChanged: + // fall through + case QQuickItem::ItemDevicePixelRatioHasChanged: q->itemChange(change, data); break; } @@ -6906,7 +6924,7 @@ void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons) } /*! - Returns whether mouse events of this item's children should be filtered + Returns whether mouse and touch events of this item's children should be filtered through this item. \sa setFiltersChildMouseEvents(), childMouseEventFilter() @@ -6918,7 +6936,7 @@ bool QQuickItem::filtersChildMouseEvents() const } /*! - Sets whether mouse events of this item's children should be filtered + Sets whether mouse and touch events of this item's children should be filtered through this item. If \a filter is true, childMouseEventFilter() will be called when @@ -7256,7 +7274,7 @@ void QQuickItem::setKeepTouchGrab(bool keep) } /*! - \qmlmethod object QtQuick::Item::contains(point point) + \qmlmethod bool QtQuick::Item::contains(point point) Returns true if this item contains \a point, which is in local coordinates; returns false otherwise. diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 8795c71b38..b33f3d8b6a 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -172,7 +172,8 @@ public: ItemOpacityHasChanged, // value.realValue ItemActiveFocusHasChanged, // value.boolValue ItemRotationHasChanged, // value.realValue - ItemAntialiasingHasChanged // value.boolValue + ItemAntialiasingHasChanged, // value.boolValue + ItemDevicePixelRatioHasChanged // value.realValue }; union ItemChangeData { diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 7009a8e328..188b347a20 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -2484,8 +2484,10 @@ void QQuickItemViewPrivate::updateUnrequestedIndexes() void QQuickItemViewPrivate::updateUnrequestedPositions() { - for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) - repositionPackageItemAt(it.key(), it.value()); + for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.cbegin(), cend = unrequestedItems.cend(); it != cend; ++it) { + if (it.value() >= 0) + repositionPackageItemAt(it.key(), it.value()); + } } void QQuickItemViewPrivate::updateVisibleIndex() diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index e42873f459..f843b09592 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -2195,7 +2195,7 @@ void QQuickListView::setOrientation(QQuickListView::Orientation orientation) existing applications. When explicitly set, it will cease to be bound to the interactive property. - \sa \l {Flickable::}{interactive} + \sa {Flickable::}{interactive} */ diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 0f7f1961a1..f3254cf8d7 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -1465,7 +1465,8 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value) { Q_D(QQuickText); Q_UNUSED(value); - if (change == ItemAntialiasingHasChanged) { + switch (change) { + case ItemAntialiasingHasChanged: if (!antialiasing()) d->font.setStyleStrategy(QFont::NoAntialias); else @@ -1473,6 +1474,22 @@ void QQuickText::itemChange(ItemChange change, const ItemChangeData &value) d->implicitWidthValid = false; d->implicitHeightValid = false; d->updateLayout(); + break; + + case ItemDevicePixelRatioHasChanged: + if (d->renderType == NativeRendering) { + // Native rendering optimizes for a given pixel grid, so its results must not be scaled. + // Text layout code respects the current device pixel ratio automatically, we only need + // to rerun layout after the ratio changed. + // Changes of implicit size should be minimal; they are hard to avoid. + d->implicitWidthValid = false; + d->implicitHeightValid = false; + d->updateLayout(); + } + break; + + default: + break; } QQuickItem::itemChange(change, value); } diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index ab460034bb..127b51948a 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -44,6 +44,7 @@ #include <qcoreapplication.h> #include <qfont.h> +#include <qfontmetrics.h> #include <qevent.h> #include <qdebug.h> #include <qdrag.h> @@ -965,6 +966,14 @@ process: { QString text = e->text(); if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) { + if (overwriteMode + // no need to call deleteChar() if we have a selection, insertText + // does it already + && !cursor.hasSelection() + && !cursor.atBlockEnd()) { + cursor.deleteChar(); + } + cursor.insertText(text); selectionChanged(); } else { @@ -1007,6 +1016,12 @@ QRectF QQuickTextControlPrivate::rectForPosition(int position) const if (line.isValid()) { qreal x = line.cursorToX(relativePos); qreal w = 0; + if (overwriteMode) { + if (relativePos < line.textLength() - line.textStart()) + w = line.cursorToX(relativePos + 1) - x; + else + w = QFontMetrics(block.layout()->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw() + } r = QRectF(layoutPos.x() + x, layoutPos.y() + line.y(), textCursorWidth + w, line.height()); } else { r = QRectF(layoutPos.x(), layoutPos.y(), textCursorWidth, 10); // #### correct height @@ -1483,6 +1498,21 @@ bool QQuickTextControl::hasImState() const return d->hasImState; } +bool QQuickTextControl::overwriteMode() const +{ + Q_D(const QQuickTextControl); + return d->overwriteMode; +} + +void QQuickTextControl::setOverwriteMode(bool overwrite) +{ + Q_D(QQuickTextControl); + if (d->overwriteMode == overwrite) + return; + d->overwriteMode = overwrite; + emit overwriteModeChanged(overwrite); +} + bool QQuickTextControl::cursorVisible() const { Q_D(const QQuickTextControl); diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 23c91d555b..cee9cff064 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -94,6 +94,8 @@ public: #endif bool hasImState() const; + bool overwriteMode() const; + void setOverwriteMode(bool overwrite); bool cursorVisible() const; void setCursorVisible(bool visible); QRectF cursorRect(const QTextCursor &cursor) const; @@ -148,6 +150,7 @@ Q_SIGNALS: void copyAvailable(bool b); void selectionChanged(); void cursorPositionChanged(); + void overwriteModeChanged(bool overwriteMode); // control signals void updateCursorRequest(); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index f6c3d2f09f..a3ceb1344c 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1584,6 +1584,32 @@ bool QQuickTextEdit::event(QEvent *event) } /*! + \qmlproperty bool QtQuick::TextEdit::overwriteMode + \since 5.8 + Whether text entered by the user will overwrite existing text. + + As with many text editors, the text editor widget can be configured + to insert or overwrite existing text with new text entered by the user. + + If this property is \c true, existing text is overwritten, character-for-character + by new text; otherwise, text is inserted at the cursor position, displacing + existing text. + + By default, this property is \c false (new text does not overwrite existing text). +*/ +bool QQuickTextEdit::overwriteMode() const +{ + Q_D(const QQuickTextEdit); + return d->control->overwriteMode(); +} + +void QQuickTextEdit::setOverwriteMode(bool overwrite) +{ + Q_D(QQuickTextEdit); + d->control->setOverwriteMode(overwrite); +} + +/*! \overload Handles the given key \a event. */ @@ -2186,6 +2212,7 @@ void QQuickTextEditPrivate::init() qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate())); qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString))); qmlobject_connect(control, QQuickTextControl, SIGNAL(linkHovered(QString)), q, QQuickTextEdit, SIGNAL(linkHovered(QString))); + qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(bool))); qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged())); qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged())); #ifndef QT_NO_CLIPBOARD diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index f6ecb984e3..42c9064860 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -86,6 +86,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) Q_PROPERTY(QQmlComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode NOTIFY overwriteModeChanged) Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged) @@ -199,6 +200,9 @@ public: QQmlComponent* cursorDelegate() const; void setCursorDelegate(QQmlComponent*); + bool overwriteMode() const; + void setOverwriteMode(bool overwrite); + int selectionStart() const; int selectionEnd() const; @@ -313,6 +317,7 @@ Q_SIGNALS: void readOnlyChanged(bool isReadOnly); void cursorVisibleChanged(bool isCursorVisible); void cursorDelegateChanged(); + void overwriteModeChanged(bool overwriteMode); void activeFocusOnPressChanged(bool activeFocusOnPressed); void persistentSelectionChanged(bool isPersistentSelection); void textMarginChanged(qreal textMargin); diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 7a86e1a323..9664326af5 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -813,7 +813,14 @@ QRectF QQuickTextInput::cursorRectangle() const return QRectF(); qreal x = l.cursorToX(c) - d->hscroll + leftPadding(); qreal y = l.y() - d->vscroll + topPadding(); - return QRectF(x, y, 1, l.height()); + qreal w = 1; + if (d->overwriteMode) { + if (c < text().length()) + w = l.cursorToX(c + 1) - x; + else + w = QFontMetrics(font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw() + } + return QRectF(x, y, w, l.height()); } /*! @@ -1263,7 +1270,14 @@ QRectF QQuickTextInput::positionToRectangle(int pos) const return QRectF(); qreal x = l.cursorToX(pos) - d->hscroll; qreal y = l.y() - d->vscroll; - return QRectF(x, y, 1, l.height()); + qreal w = 1; + if (d->overwriteMode) { + if (pos < text().length()) + w = l.cursorToX(pos + 1) - x; + else + w = QFontMetrics(font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw() + } + return QRectF(x, y, w, l.height()); } /*! @@ -1345,6 +1359,36 @@ int QQuickTextInputPrivate::positionAt(qreal x, qreal y, QTextLine::CursorPositi return line.isValid() ? line.xToCursor(x, position) : 0; } +/*! + \qmlproperty bool QtQuick::TextInput::overwriteMode + \since 5.8 + + Whether text entered by the user will overwrite existing text. + + As with many text editors, the text editor widget can be configured + to insert or overwrite existing text with new text entered by the user. + + If this property is \c true, existing text is overwritten, character-for-character + by new text; otherwise, text is inserted at the cursor position, displacing + existing text. + + By default, this property is \c false (new text does not overwrite existing text). +*/ +bool QQuickTextInput::overwriteMode() const +{ + Q_D(const QQuickTextInput); + return d->overwriteMode; +} + +void QQuickTextInput::setOverwriteMode(bool overwrite) +{ + Q_D(QQuickTextInput); + if (d->overwriteMode == overwrite) + return; + d->overwriteMode = overwrite; + emit overwriteModeChanged(overwrite); +} + void QQuickTextInput::keyPressEvent(QKeyEvent* ev) { Q_D(QQuickTextInput); @@ -4409,6 +4453,14 @@ void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event) if (unknown && !m_readOnly) { QString t = event->text(); if (!t.isEmpty() && t.at(0).isPrint()) { + if (overwriteMode + // no need to call del() if we have a selection, insert + // does it already + && !hasSelectedText() + && !(m_cursor == q_func()->text().length())) { + del(); + } + insert(t); event->accept(); return; diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 06ca1acb0d..d2dee2c284 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -79,6 +79,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged) Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) Q_PROPERTY(QQmlComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged) + Q_PROPERTY(bool overwriteMode READ overwriteMode WRITE setOverwriteMode NOTIFY overwriteModeChanged) Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged) Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged) Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged) @@ -245,6 +246,9 @@ public: QQmlComponent* cursorDelegate() const; void setCursorDelegate(QQmlComponent*); + bool overwriteMode() const; + void setOverwriteMode(bool overwrite); + bool focusOnPress() const; void setFocusOnPress(bool); @@ -325,6 +329,7 @@ Q_SIGNALS: void readOnlyChanged(bool isReadOnly); void cursorVisibleChanged(bool isCursorVisible); void cursorDelegateChanged(); + void overwriteModeChanged(bool overwriteMode); void maximumLengthChanged(int maximumLength); void validatorChanged(); void inputMaskChanged(const QString &inputMask); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 24cbc7f74b..d5a138945d 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -158,6 +158,7 @@ public: , m_passwordEchoEditing(false) , inLayout(false) , requireImplicitWidth(false) + , overwriteMode(false) { } @@ -299,6 +300,7 @@ public: bool m_passwordEchoEditing : 1; bool inLayout:1; bool requireImplicitWidth:1; + bool overwriteMode:1; static inline QQuickTextInputPrivate *get(QQuickTextInput *t) { return t->d_func(); diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index 34b9d6efa2..7b600adf53 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -731,10 +731,13 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN QVector<QPointF> glyphPositions = glyphRun.positions(); glyphPositions.reserve(count); + QRectF glyphBoundingRect = glyphRun.boundingRect(); + for (int j = 1; j < nodes.size(); ++j) { BinaryTreeNode *otherNode = nodes.at(j); glyphIndexes += otherNode->glyphRun.glyphIndexes(); primaryNode->ranges += otherNode->ranges; + glyphBoundingRect = glyphBoundingRect.united(otherNode->glyphRun.boundingRect()); QVector<QPointF> otherPositions = otherNode->glyphRun.positions(); for (int k = 0; k < otherPositions.size(); ++k) @@ -746,6 +749,7 @@ void QQuickTextNodeEngine::mergeProcessedNodes(QList<BinaryTreeNode *> *regularN glyphRun.setGlyphIndexes(glyphIndexes); glyphRun.setPositions(glyphPositions); + glyphRun.setBoundingRect(glyphBoundingRect); } } } @@ -828,14 +832,15 @@ void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode, for (int i = 0; i < node->ranges.size(); ++i) { const QPair<int, int> &range = node->ranges.at(i); - int rangeLength = range.second - range.first; + int rangeLength = range.second - range.first + 1; if (previousNode != 0) { for (int j = 0; j < previousNode->ranges.size(); ++j) { const QPair<int, int> &otherRange = previousNode->ranges.at(j); + if (range.first < otherRange.second && range.second > otherRange.first) { int start = qMax(range.first, otherRange.first); int end = qMin(range.second, otherRange.second); - rangeLength -= end - start; + rangeLength -= end - start + 1; if (rangeLength == 0) break; } @@ -849,7 +854,7 @@ void QQuickTextNodeEngine::addToSceneGraph(QQuickTextNode *parentNode, if (range.first < otherRange.second && range.second > otherRange.first) { int start = qMax(range.first, otherRange.first); int end = qMin(range.second, otherRange.second); - rangeLength -= end - start; + rangeLength -= end - start + 1; if (rangeLength == 0) break; } diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 635f1da77e..b6d685fdf0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -294,10 +294,43 @@ void QQuickWindow::update() QQuickRenderControlPrivate::get(d->renderControl)->update(); } +static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio) +{ + if (item->flags() & QQuickItem::ItemHasContents) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio); + } + + QList <QQuickItem *> items = item->childItems(); + for (int i = 0; i < items.size(); ++i) + updatePixelRatioHelper(items.at(i), pixelRatio); +} + +void QQuickWindow::physicalDpiChanged() +{ + Q_D(QQuickWindow); + const qreal newPixelRatio = screen()->devicePixelRatio(); + if (qFuzzyCompare(newPixelRatio, d->devicePixelRatio)) + return; + d->devicePixelRatio = newPixelRatio; + if (d->contentItem) + updatePixelRatioHelper(d->contentItem, newPixelRatio); +} + void QQuickWindow::handleScreenChanged(QScreen *screen) { Q_D(QQuickWindow); - Q_UNUSED(screen) + if (screen) { + physicalDpiChanged(); + // When physical DPI changes on the same screen, either the resolution or the device pixel + // ratio changed. We must check what it is. Device pixel ratio does not have its own + // ...Changed() signal. + d->physicalDpiChangedConnection = connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)), + this, SLOT(physicalDpiChanged())); + } else { + disconnect(d->physicalDpiChangedConnection); + } + d->forcePolish(); } @@ -416,6 +449,7 @@ QQuickWindowPrivate::QQuickWindowPrivate() , touchMouseId(-1) , touchMousePressTimestamp(0) , dirtyItemList(0) + , devicePixelRatio(0) , context(0) , renderer(0) , windowManager(0) @@ -470,6 +504,9 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) Q_ASSERT(windowManager || renderControl); + if (QScreen *screen = q->screen()) + devicePixelRatio = screen->devicePixelRatio(); + QSGContext *sg; if (renderControl) { QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl); @@ -2114,7 +2151,7 @@ bool QQuickWindowPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *even return (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()); } -// touchEventForItemBounds has no means to generate a touch event that contains +// touchEventForItem has no means to generate a touch event that contains // only the points that are relevant for this item. Thus the need for // matchingPoints to already be that set of interesting points. // They are all pre-transformed, too. @@ -2176,7 +2213,7 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv return touchEventAccepted; } -QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent) +QTouchEvent *QQuickWindowPrivate::touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds) { const QList<QTouchEvent::TouchPoint> &touchPoints = originalEvent.touchPoints(); QList<QTouchEvent::TouchPoint> pointsInBounds; @@ -2184,7 +2221,10 @@ QTouchEvent *QQuickWindowPrivate::touchEventForItemBounds(QQuickItem *target, co if (originalEvent.touchPointStates() != Qt::TouchPointStationary) { for (int i = 0; i < touchPoints.count(); ++i) { const QTouchEvent::TouchPoint &tp = touchPoints.at(i); - if (tp.state() == Qt::TouchPointPressed) { + // Touch presses are relevant to the target item only if they occur inside its bounds. + // Touch updates and releases are relevant if they occur inside, or if we want to + // finish the sequence because the press occurred inside. + if (tp.state() == Qt::TouchPointPressed || alwaysCheckBounds) { QPointF p = target->mapFromScene(tp.scenePos()); if (target->contains(p)) pointsInBounds.append(tp); @@ -2413,7 +2453,7 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { hasFiltered->insert(target); - QScopedPointer<QTouchEvent> targetEvent(touchEventForItemBounds(target, *event)); + QScopedPointer<QTouchEvent> targetEvent(touchEventForItem(target, *event)); if (!targetEvent->touchPoints().isEmpty()) { if (target->childMouseEventFilter(item, targetEvent.data())) { qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; @@ -2513,6 +2553,15 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent return overThreshold; } +bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold) +{ + QStyleHints *styleHints = qApp->styleHints(); + bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance()); + qreal velocity = axis == Qt::XAxis ? tp->velocity().x() : tp->velocity().y(); + overThreshold |= qAbs(velocity) > styleHints->startDragVelocity(); + return overThreshold; +} + /*! \qmlproperty list<Object> Window::data \default @@ -3872,6 +3921,14 @@ void QQuickWindow::resetOpenGLState() */ /*! + \qmlattachedproperty Window Window::window + \since 5.7 + + This attached property holds the item's window. + The Window attached property can be attached to any Item. +*/ + +/*! \qmlattachedproperty int Window::width \qmlattachedproperty int Window::height \since 5.5 diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 262828ad1d..e04a4a1ce2 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -202,6 +202,7 @@ protected: private Q_SLOTS: void maybeUpdate(); void cleanupSceneGraph(); + void physicalDpiChanged(); void handleScreenChanged(QScreen *screen); void setTransientParent_helper(QQuickWindow *window); void runJobsAfterSwap(); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 652320c1c9..1486e20e1e 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -163,8 +163,8 @@ public: void flushDelayedTouchEvent(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints, QSet<QQuickItem*> *filtered); - QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent); - QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints); + static QTouchEvent *touchEventForItem(QQuickItem *target, const QTouchEvent &originalEvent, bool alwaysCheckBounds = false); + static QTouchEvent *touchEventWithPoints(const QTouchEvent &event, const QList<QTouchEvent::TouchPoint> &newPoints); bool sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QTouchEvent *event, QSet<QQuickItem*> *filtered); bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool accepted); @@ -211,6 +211,9 @@ public: QVector<QQuickItem *> itemsToPolish; + qreal devicePixelRatio; + QMetaObject::Connection physicalDpiChangedConnection; + void updateDirtyNodes(); void cleanupNodes(); void cleanupNodesOnShutdown(); @@ -262,6 +265,7 @@ public: static bool defaultAlphaBuffer; static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold = -1); + static bool dragOverThreshold(qreal d, Qt::Axis axis, const QTouchEvent::TouchPoint *tp, int startDragThreshold = -1); // data property static void data_append(QQmlListProperty<QObject> *, QObject *); diff --git a/src/quick/items/qquickwindowattached.cpp b/src/quick/items/qquickwindowattached.cpp index e1cd3c82f2..c6380e2b9a 100644 --- a/src/quick/items/qquickwindowattached.cpp +++ b/src/quick/items/qquickwindowattached.cpp @@ -51,9 +51,9 @@ QQuickWindowAttached::QQuickWindowAttached(QObject* attachee) { m_attachee = qobject_cast<QQuickItem*>(attachee); if (m_attachee && m_attachee->window()) // It might not be in a window yet - windowChanged(m_attachee->window()); + windowChange(m_attachee->window()); if (m_attachee) - connect(m_attachee, &QQuickItem::windowChanged, this, &QQuickWindowAttached::windowChanged); + connect(m_attachee, &QQuickItem::windowChanged, this, &QQuickWindowAttached::windowChange); } QWindow::Visibility QQuickWindowAttached::visibility() const @@ -86,7 +86,12 @@ int QQuickWindowAttached::height() const return (m_window ? m_window->height() : 0); } -void QQuickWindowAttached::windowChanged(QQuickWindow *window) +QQuickWindow *QQuickWindowAttached::window() const +{ + return m_window; +} + +void QQuickWindowAttached::windowChange(QQuickWindow *window) { if (window != m_window) { QQuickWindow* oldWindow = m_window; @@ -95,21 +100,23 @@ void QQuickWindowAttached::windowChanged(QQuickWindow *window) if (oldWindow) oldWindow->disconnect(this); - if (!window) - return; // No values to get, therefore nothing to emit + emit windowChanged(); - if (!oldWindow || window->visibility() != oldWindow->visibility()) + if (!oldWindow || !window || window->visibility() != oldWindow->visibility()) emit visibilityChanged(); - if (!oldWindow || window->isActive() != oldWindow->isActive()) + if (!oldWindow || !window || window->isActive() != oldWindow->isActive()) emit activeChanged(); - if (!oldWindow || window->activeFocusItem() != oldWindow->activeFocusItem()) + if (!oldWindow || !window || window->activeFocusItem() != oldWindow->activeFocusItem()) emit activeFocusItemChanged(); emit contentItemChanged(); - if (!oldWindow || window->width() != oldWindow->width()) + if (!oldWindow || !window || window->width() != oldWindow->width()) emit widthChanged(); - if (!oldWindow || window->height() != oldWindow->height()) + if (!oldWindow || !window || window->height() != oldWindow->height()) emit heightChanged(); + if (!window) + return; + // QQuickWindowQmlImpl::visibilityChanged also exists, and window might even // be QQuickWindowQmlImpl, but that's not what we are connecting to. // So this is actual window state rather than a buffered or as-requested one. diff --git a/src/quick/items/qquickwindowattached_p.h b/src/quick/items/qquickwindowattached_p.h index 5e4b2c1721..3212508fd8 100644 --- a/src/quick/items/qquickwindowattached_p.h +++ b/src/quick/items/qquickwindowattached_p.h @@ -69,6 +69,7 @@ class Q_AUTOTEST_EXPORT QQuickWindowAttached : public QObject Q_PROPERTY(QQuickItem* contentItem READ contentItem NOTIFY contentItemChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(int height READ height NOTIFY heightChanged) + Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged) public: QQuickWindowAttached(QObject* attachee); @@ -79,6 +80,7 @@ public: QQuickItem* contentItem() const; int width() const; int height() const; + QQuickWindow *window() const; Q_SIGNALS: @@ -88,9 +90,10 @@ Q_SIGNALS: void contentItemChanged(); void widthChanged(); void heightChanged(); + void windowChanged(); protected Q_SLOTS: - void windowChanged(QQuickWindow*); + void windowChange(QQuickWindow*); private: QQuickWindow* m_window; diff --git a/src/quick/quick.pro b/src/quick/quick.pro index 4ff2d76a09..90ba867ea8 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -1,7 +1,12 @@ TARGET = QtQuick QT = core-private gui-private qml-private -QT_PRIVATE = network +!no_network { + QT_PRIVATE = network +} +no_network { + DEFINES += QT_NO_NETWORK +} DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS @@ -24,12 +29,10 @@ ANDROID_BUNDLED_FILES += \ qml \ lib/libQt5QuickParticles.so -load(qt_module) - include(util/util.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) -include(designer/designer.pri) +!wince:include(designer/designer.pri) contains(QT_CONFIG, accessibility) { include(accessible/accessible.pri) } @@ -43,3 +46,5 @@ SOURCES += qtquick2.cpp # To make #include "qquickcontext2d_jsclass.cpp" work INCLUDEPATH += $$PWD + +load(qt_module) diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index a85a9ab3eb..bb5476c3e2 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -2698,7 +2698,9 @@ void Renderer::render() if (m_alphaBatches.size()) std::sort(&m_alphaBatches.first(), &m_alphaBatches.last() + 1, qsg_sort_batch_increasing_order); - m_zRange = 1.0 / (m_nextRenderOrder); + m_zRange = m_nextRenderOrder != 0 + ? 1.0 / (m_nextRenderOrder) + : 0; } if (Q_UNLIKELY(debug_render())) timeSorting = timer.restart(); diff --git a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp index 1112eca27b..97d7e69407 100644 --- a/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp +++ b/src/quick/scenegraph/coreapi/qsgshaderrewriter.cpp @@ -274,7 +274,7 @@ int main(int argc, char **argv) for (int i=0; i<args.length(); ++i) { const QString &a = args.at(i); - if (a == QStringLiteral("--file") && i < args.length() - 1) { + if (a == QLatin1String("--file") && i < args.length() - 1) { qDebug() << "Reading file: " << args.at(i); QFile file(args.at(++i)); if (!file.open(QFile::ReadOnly)) { @@ -282,10 +282,10 @@ int main(int argc, char **argv) return 1; } content = file.readAll(); - } else if (a == QStringLiteral("--selftest")) { + } else if (a == QLatin1String("--selftest")) { qDebug() << "doing a selftest"; content = QByteArray(selftest); - } else if (a == QStringLiteral("--help") || a == QStringLiteral("-h")) { + } else if (a == QLatin1String("--help") || a == QLatin1String("-h")) { qDebug() << "usage:" << endl << " --file [name] A vertex shader file to rewrite" << endl; } diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index 6572ceb2ce..2ba16e7328 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -136,9 +136,9 @@ public: bool distanceFieldAntialiasingDecided; }; -static bool qsg_useConsistentTiming() +bool qsg_useConsistentTiming() { - static int use = -1; + int use = -1; if (use < 0) { use = !qEnvironmentVariableIsEmpty("QSG_FIXED_ANIMATION_STEP") && qgetenv("QSG_FIXED_ANIMATION_STEP") != "no" ? 1 : 0; diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index b5f149eff7..cd0f77799b 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE - +extern bool qsg_useConsistentTiming(); extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); /*! @@ -267,6 +267,10 @@ void QSGRenderLoop::handleContextCreationFailure(QQuickWindow *window, QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop() : gl(0) { + if (qsg_useConsistentTiming()) { + QUnifiedTimer::instance(true)->setConsistentTiming(true); + qCDebug(QSG_LOG_INFO, "using fixed animation steps"); + } sg = QSGContext::createDefaultContext(); rc = sg->createRenderContext(); } diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 9d6e7f3b2a..57468e5799 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -141,9 +141,9 @@ Atlas::Atlas(const QSize &size) QString *deviceName = static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName")); static bool wrongfullyReportsBgra8888Support = deviceName != 0 - && (deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0); + && (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0); #else static bool wrongfullyReportsBgra8888Support = false; // The Raspberry Pi (both 1 and 2) GPU refuses framebuffers with BGRA color attachments. diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 169f3ebe59..74390334c4 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -720,9 +720,9 @@ void QSGPlainTexture::bind() QString *deviceName = static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName")); static bool wrongfullyReportsBgra8888Support = deviceName != 0 - && (deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0 - || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0); + && (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0 + || deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0); #else static bool wrongfullyReportsBgra8888Support = false; #endif diff --git a/src/quick/util/qquickanimator.cpp b/src/quick/util/qquickanimator.cpp index f0d4caf7ab..abae6321b0 100644 --- a/src/quick/util/qquickanimator.cpp +++ b/src/quick/util/qquickanimator.cpp @@ -217,7 +217,8 @@ qreal QQuickAnimator::from() const void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, - QQmlProperties &modified) + QQmlProperties &modified, + QObject *defaultTarget) { if (actions.size()) { @@ -249,14 +250,20 @@ void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, // the item when a transition is cancelled. action.fromValue = action.toValue; } - } else { + } + + if (modified.isEmpty()) { job->setTarget(target); job->setFrom(from); job->setTo(to); } - if (!job->target() && defaultProperty.object()) - job->setTarget(qobject_cast<QQuickItem *>(defaultProperty.object())); + if (!job->target()) { + if (defaultProperty.object()) + job->setTarget(qobject_cast<QQuickItem *>(defaultProperty.object())); + else + job->setTarget(qobject_cast<QQuickItem *>(defaultTarget)); + } job->setDuration(duration); job->setLoopCount(loopCount); @@ -266,7 +273,7 @@ void QQuickAnimatorPrivate::apply(QQuickAnimatorJob *job, QAbstractAnimationJob *QQuickAnimator::transition(QQuickStateActions &actions, QQmlProperties &modified, TransitionDirection direction, - QObject *) + QObject *defaultTarget) { Q_D(QQuickAnimator); @@ -283,7 +290,7 @@ QAbstractAnimationJob *QQuickAnimator::transition(QQuickStateActions &actions, if (!job) return 0; - d->apply(job, propertyName(), actions, modified); + d->apply(job, propertyName(), actions, modified, defaultTarget); if (!job->target()) { delete job; diff --git a/src/quick/util/qquickanimator_p_p.h b/src/quick/util/qquickanimator_p_p.h index 7a61462762..b176119c70 100644 --- a/src/quick/util/qquickanimator_p_p.h +++ b/src/quick/util/qquickanimator_p_p.h @@ -82,7 +82,7 @@ public: uint isFromDefined : 1; uint isToDefined : 1; - void apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, QQmlProperties &modified); + void apply(QQuickAnimatorJob *job, const QString &propertyName, QQuickStateActions &actions, QQmlProperties &modified, QObject *defaultTarget); }; class QQuickRotationAnimatorPrivate : public QQuickAnimatorPrivate diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp index a4e2f0eb0e..5c26b23ff7 100644 --- a/src/quick/util/qquickapplication.cpp +++ b/src/quick/util/qquickapplication.cpp @@ -90,4 +90,9 @@ Qt::ApplicationState QQuickApplication::state() const return QGuiApplication::applicationState(); } +QFont QQuickApplication::font() const +{ + return QGuiApplication::font(); +} + QT_END_NAMESPACE diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h index 971c9a203a..091cb094ed 100644 --- a/src/quick/util/qquickapplication_p.h +++ b/src/quick/util/qquickapplication_p.h @@ -52,6 +52,7 @@ // #include <QtCore/QObject> +#include <QtGui/QFont> #include <qqml.h> #include <QtQml/private/qqmlglobal_p.h> #include <private/qtquickglobal_p.h> @@ -66,6 +67,7 @@ class Q_AUTOTEST_EXPORT QQuickApplication : public QQmlApplication Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged) Q_PROPERTY(bool supportsMultipleWindows READ supportsMultipleWindows CONSTANT) Q_PROPERTY(Qt::ApplicationState state READ state NOTIFY stateChanged) + Q_PROPERTY(QFont font READ font CONSTANT) public: explicit QQuickApplication(QObject *parent = 0); @@ -74,6 +76,7 @@ public: Qt::LayoutDirection layoutDirection() const; bool supportsMultipleWindows() const; Qt::ApplicationState state() const; + QFont font() const; Q_SIGNALS: void activeChanged(); diff --git a/src/quick/util/qquickfontloader.cpp b/src/quick/util/qquickfontloader.cpp index 64fd458ef0..b7367f3934 100644 --- a/src/quick/util/qquickfontloader.cpp +++ b/src/quick/util/qquickfontloader.cpp @@ -45,14 +45,18 @@ #include <QStringList> #include <QUrl> #include <QDebug> -#include <QNetworkRequest> -#include <QNetworkReply> + #include <QFontDatabase> #include <private/qobject_p.h> #include <qqmlinfo.h> #include <qqmlfile.h> +#ifndef QT_NO_NETWORK +#include <QNetworkRequest> +#include <QNetworkReply> +#endif + #include <QtCore/QCoreApplication> QT_BEGIN_NAMESPACE @@ -66,28 +70,37 @@ Q_OBJECT public: explicit QQuickFontObject(int _id = -1); +#ifndef QT_NO_NETWORK void download(const QUrl &url, QNetworkAccessManager *manager); Q_SIGNALS: void fontDownloaded(const QString&, QQuickFontLoader::Status); +private: + int redirectCount; + QNetworkReply *reply; + private Q_SLOTS: void replyFinished(); +#endif // QT_NO_NETWORK public: int id; -private: - QNetworkReply *reply; - int redirectCount; - Q_DISABLE_COPY(QQuickFontObject) }; QQuickFontObject::QQuickFontObject(int _id) - : QObject(0), id(_id), reply(0), redirectCount(0) {} + : QObject(0) +#ifndef QT_NO_NETWORK + ,redirectCount(0), reply(0) +#endif + ,id(_id) +{ +} +#ifndef QT_NO_NETWORK void QQuickFontObject::download(const QUrl &url, QNetworkAccessManager *manager) { QNetworkRequest req(url); @@ -128,7 +141,7 @@ void QQuickFontObject::replyFinished() reply = 0; } } - +#endif // QT_NO_NETWORK class QQuickFontLoaderPrivate : public QObjectPrivate { @@ -255,6 +268,7 @@ void QQuickFontLoader::setSource(const QUrl &url) } } else { if (!fontLoaderFonts()->map.contains(d->url)) { +#ifndef QT_NO_NETWORK QQuickFontObject *fo = new QQuickFontObject; fontLoaderFonts()->map[d->url] = fo; fo->download(d->url, qmlEngine(this)->networkAccessManager()); @@ -262,13 +276,20 @@ void QQuickFontLoader::setSource(const QUrl &url) emit statusChanged(); QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)), this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status))); +#else +// Silently fail if compiled with no_network +#endif } else { QQuickFontObject *fo = fontLoaderFonts()->map[d->url]; if (fo->id == -1) { +#ifndef QT_NO_NETWORK d->status = Loading; emit statusChanged(); QObject::connect(fo, SIGNAL(fontDownloaded(QString,QQuickFontLoader::Status)), this, SLOT(updateFontInfo(QString,QQuickFontLoader::Status))); +#else +// Silently fail if compiled with no_network +#endif } else updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index d669a7feda..23b2887fac 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qquickpixmapcache_p.h" -#include <qqmlnetworkaccessmanagerfactory.h> #include <qquickimageprovider.h> #include <qqmlengine.h> @@ -55,7 +54,6 @@ #include <QCoreApplication> #include <QImageReader> #include <QHash> -#include <QNetworkReply> #include <QPixmapCache> #include <QFile> #include <QThread> @@ -66,10 +64,15 @@ #include <QWaitCondition> #include <QtCore/qdebug.h> #include <private/qobject_p.h> -#include <QSslError> #include <QQmlFile> #include <QMetaMethod> +#ifndef QT_NO_NETWORK +#include <qqmlnetworkaccessmanagerfactory.h> +#include <QNetworkReply> +#include <QSslError> +#endif + #include <private/qquickprofiler_p.h> #define IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT 8 @@ -203,7 +206,9 @@ private: friend class QQuickPixmapReaderThreadObject; void processJobs(); void processJob(QQuickPixmapReply *, const QUrl &, const QString &, AutoTransform, QQuickImageProvider::ImageType, QQuickImageProvider *); +#ifndef QT_NO_NETWORK void networkRequestDone(QNetworkReply *); +#endif void asyncResponseFinished(QQuickImageResponse *); QList<QQuickPixmapReply*> jobs; @@ -215,10 +220,11 @@ private: QQuickPixmapReaderThreadObject *threadObject; QWaitCondition waitCondition; +#ifndef QT_NO_NETWORK QNetworkAccessManager *networkAccessManager(); QNetworkAccessManager *accessManager; - QHash<QNetworkReply*,QQuickPixmapReply*> networkJobs; +#endif QHash<QQuickImageResponse*,QQuickPixmapReply*> asyncResponses; static int replyDownloadProgress; @@ -343,6 +349,7 @@ QQuickPixmapReply::Event::~Event() delete textureFactory; } +#ifndef QT_NO_NETWORK QNetworkAccessManager *QQuickPixmapReader::networkAccessManager() { if (!accessManager) { @@ -351,6 +358,7 @@ QNetworkAccessManager *QQuickPixmapReader::networkAccessManager() } return accessManager; } +#endif static void maybeRemoveAlpha(QImage *image) { @@ -421,7 +429,10 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e } QQuickPixmapReader::QQuickPixmapReader(QQmlEngine *eng) -: QThread(eng), engine(eng), threadObject(0), accessManager(0) +: QThread(eng), engine(eng), threadObject(0) +#ifndef QT_NO_NETWORK +, accessManager(0) +#endif { eventLoopQuitHack = new QObject; eventLoopQuitHack->moveToThread(this); @@ -443,6 +454,7 @@ QQuickPixmapReader::~QQuickPixmapReader() delete reply; } jobs.clear(); +#ifndef QT_NO_NETWORK QList<QQuickPixmapReply*> activeJobs = networkJobs.values() + asyncResponses.values(); foreach (QQuickPixmapReply *reply, activeJobs ) { if (reply->loading) { @@ -450,6 +462,7 @@ QQuickPixmapReader::~QQuickPixmapReader() reply->data = 0; } } +#endif if (threadObject) threadObject->processJobs(); mutex.unlock(); @@ -457,6 +470,7 @@ QQuickPixmapReader::~QQuickPixmapReader() wait(); } +#ifndef QT_NO_NETWORK void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) { QQuickPixmapReply *job = networkJobs.take(reply); @@ -506,6 +520,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply) // kick off event loop again incase we have dropped below max request count threadObject->processJobs(); } +#endif // QT_NO_NETWORK void QQuickPixmapReader::asyncResponseFinished(QQuickImageResponse *response) { @@ -557,8 +572,10 @@ bool QQuickPixmapReaderThreadObject::event(QEvent *e) void QQuickPixmapReaderThreadObject::networkRequestDone() { +#ifndef QT_NO_NETWORK QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reader->networkRequestDone(reply); +#endif } void QQuickPixmapReaderThreadObject::asyncResponseFinished() @@ -577,6 +594,7 @@ void QQuickPixmapReader::processJobs() // Clean cancelled jobs if (!cancelled.isEmpty()) { +#ifndef QT_NO_NETWORK for (int i = 0; i < cancelled.count(); ++i) { QQuickPixmapReply *job = cancelled.at(i); QNetworkReply *reply = networkJobs.key(job, 0); @@ -599,6 +617,7 @@ void QQuickPixmapReader::processJobs() job->deleteLater(); } cancelled.clear(); +#endif } if (!jobs.isEmpty()) { @@ -619,7 +638,11 @@ void QQuickPixmapReader::processJobs() usableJob = true; } else { localFile = QQmlFile::urlToLocalFileOrQrc(url); - usableJob = !localFile.isEmpty() || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT; + usableJob = !localFile.isEmpty() +#ifndef QT_NO_NETWORK + || networkJobs.count() < IMAGEREQUEST_MAX_NETWORK_REQUEST_COUNT +#endif + ; } @@ -744,6 +767,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u runningJob->postReply(errorCode, errorStr, readSize, QQuickTextureFactory::textureFactoryForImage(image)); mutex.unlock(); } else { +#ifndef QT_NO_NETWORK // Network resource QNetworkRequest req(url); req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); @@ -753,6 +777,9 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); networkJobs.insert(reply, runningJob); +#else +// Silently fail if compiled with no_network +#endif } } } @@ -809,11 +836,13 @@ void QQuickPixmapReader::cancel(QQuickPixmapReply *reply) void QQuickPixmapReader::run() { if (replyDownloadProgress == -1) { +#ifndef QT_NO_NETWORK replyDownloadProgress = QMetaMethod::fromSignal(&QNetworkReply::downloadProgress).methodIndex(); replyFinished = QMetaMethod::fromSignal(&QNetworkReply::finished).methodIndex(); - downloadProgress = QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex(); const QMetaObject *ir = &QQuickPixmapReaderThreadObject::staticMetaObject; threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()"); +#endif + downloadProgress = QMetaMethod::fromSignal(&QQuickPixmapReply::downloadProgress).methodIndex(); } mutex.lock(); @@ -1194,8 +1223,6 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q *ok = true; return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, autoTransform, appliedTransform); } - errorString = QQuickPixmap::tr("Invalid image data: %1").arg(url.toString()); - } else { errorString = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); } diff --git a/src/quick/util/qquickstate_p.h b/src/quick/util/qquickstate_p.h index 5c33d4b6b5..7d22ca9f8c 100644 --- a/src/quick/util/qquickstate_p.h +++ b/src/quick/util/qquickstate_p.h @@ -64,7 +64,7 @@ class QQuickStateActionEvent; class QQmlBinding; class QQmlExpression; -class QQuickStateAction +class Q_QUICK_PRIVATE_EXPORT QQuickStateAction { public: QQuickStateAction(); diff --git a/src/quickwidgets/quickwidgets.pro b/src/quickwidgets/quickwidgets.pro index ab388ff9d3..87409e31c5 100644 --- a/src/quickwidgets/quickwidgets.pro +++ b/src/quickwidgets/quickwidgets.pro @@ -4,9 +4,6 @@ QT = core-private gui-private qml-private quick-private widgets-private DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES - -load(qt_module) - HEADERS += \ qquickwidget.h \ qquickwidget_p.h \ @@ -15,5 +12,4 @@ HEADERS += \ SOURCES += \ qquickwidget.cpp - - +load(qt_module) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index fe3efcd948..253b050b15 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -185,6 +185,8 @@ private slots: void argumentEvaluationOrder(); + void v4FunctionWithoutQML(); + signals: void testSignal(); }; @@ -3794,6 +3796,30 @@ void tst_QJSEngine::argumentEvaluationOrder() } +class TestObject : public QObject +{ + Q_OBJECT +public: + TestObject() : called(false) {} + + bool called; + + Q_INVOKABLE void callMe(QQmlV4Function *) { + called = true; + } +}; + +void tst_QJSEngine::v4FunctionWithoutQML() +{ + TestObject obj; + QJSEngine engine; + QJSValue wrapper = engine.newQObject(&obj); + QQmlEngine::setObjectOwnership(&obj, QQmlEngine::CppOwnership); + QVERIFY(!obj.called); + wrapper.property("callMe").call(); + QVERIFY(obj.called); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qqmlbinding/data/disabledReadonly.qml b/tests/auto/qml/qqmlbinding/data/disabledReadonly.qml new file mode 100644 index 0000000000..2d1715364c --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/disabledReadonly.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + id: root + + readonly property string name: "John" + + Binding { + target: root + property: "name" + value: "Doe" + when: false + } +} diff --git a/tests/auto/qml/qqmlbinding/data/disabledUnknown.qml b/tests/auto/qml/qqmlbinding/data/disabledUnknown.qml new file mode 100644 index 0000000000..08e1cff5d1 --- /dev/null +++ b/tests/auto/qml/qqmlbinding/data/disabledUnknown.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Item { + id: root + + Binding { + target: root + property: "unknown" + value: 42 + when: false + } +} diff --git a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp index 6eff3cce1f..3e8dfbdb12 100644 --- a/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp +++ b/tests/auto/qml/qqmlbinding/tst_qqmlbinding.cpp @@ -47,6 +47,8 @@ private slots: void deletedObject(); void warningOnUnknownProperty(); void warningOnReadOnlyProperty(); + void disabledOnUnknownProperty(); + void disabledOnReadonlyProperty(); private: QQmlEngine engine; @@ -253,6 +255,32 @@ void tst_qqmlbinding::warningOnReadOnlyProperty() QCOMPARE(messageHandler.messages().first(), expectedMessage); } +void tst_qqmlbinding::disabledOnUnknownProperty() +{ + QQmlTestMessageHandler messageHandler; + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("disabledUnknown.qml")); + QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QVERIFY(item); + delete item; + + QCOMPARE(messageHandler.messages().count(), 0); +} + +void tst_qqmlbinding::disabledOnReadonlyProperty() +{ + QQmlTestMessageHandler messageHandler; + + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("disabledReadonly.qml")); + QQuickItem *item = qobject_cast<QQuickItem*>(c.create()); + QVERIFY(item); + delete item; + + QCOMPARE(messageHandler.messages().count(), 0); +} + QTEST_MAIN(tst_qqmlbinding) #include "tst_qqmlbinding.moc" diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml new file mode 100644 index 0000000000..b64a2e23c0 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_arg.qml @@ -0,0 +1,11 @@ +import QtQuick 2.7 +import Test 1.0 + +MyArrayBufferTestClass { + property bool ok: false + Component.onCompleted: { + var data = new Uint8Array([1, 2, 3]); + var sum = byteArrayMethod_Sum(data.buffer); + ok = sum == 6; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml new file mode 100644 index 0000000000..9e1c91810a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_overload.qml @@ -0,0 +1,7 @@ +import QtQuick 2.7 +import Test 1.0 + +MyArrayBufferTestClass { + property bool ok: false + Component.onCompleted: ok = byteArrayMethod_Overloaded(new ArrayBuffer()); +} diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml new file mode 100644 index 0000000000..5a4f9fec0b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_method_return.qml @@ -0,0 +1,15 @@ +import QtQuick 2.7 +import Test 1.0 + +MyArrayBufferTestClass { + property bool ok: false + Component.onCompleted: { + var buf = byteArrayMethod_CountUp(1, 3); + var view = new DataView(buf); + ok = buf instanceof ArrayBuffer + && buf.byteLength == 3 + && view.getUint8(0) == 1 + && view.getUint8(1) == 2 + && view.getUint8(2) == 3; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml new file mode 100644 index 0000000000..78ebb1abe1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_get.qml @@ -0,0 +1,5 @@ +import Test 1.0 + +MyArrayBufferTestClass { + property bool ok: byteArrayProperty instanceof ArrayBuffer +} diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml new file mode 100644 index 0000000000..e8a51273ca --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_property_set.qml @@ -0,0 +1,11 @@ +import QtQuick 2.7 +import Test 1.0 + +MyArrayBufferTestClass { + property bool ok: false + onByteArraySignal: ok = byteArrayProperty instanceof ArrayBuffer + Component.onCompleted: { + byteArrayProperty = new ArrayBuffer(42); + ok = byteArrayProperty instanceof ArrayBuffer && byteArrayProperty.byteLength == 42; + } +} diff --git a/tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml b/tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml new file mode 100644 index 0000000000..d9f436e788 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/arraybuffer_signal_arg.qml @@ -0,0 +1,14 @@ +import QtQuick 2.7 +import Test 1.0 + +MyArrayBufferTestClass { + property bool ok: false + onByteArraySignal: { + var view = new DataView(arg); + ok = arg instanceof ArrayBuffer + && arg.byteLength == 2 + && view.getUint8(0) == 42 + && view.getUint8(1) == 43; + } + Component.onCompleted: emitByteArraySignal(42, 2) +} diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.1.errors.txt b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.1.errors.txt new file mode 100644 index 0000000000..33360e96cf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.1.errors.txt @@ -0,0 +1 @@ +5:5:Invalid property assignment: Enum value "lowercaseEnumVal" cannot start with a lowercase letter diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.1.qml b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.1.qml new file mode 100644 index 0000000000..f6c3e9b404 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.1.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import Test 1.0 + +MyTypeObject { + intProperty: MyTypeObject.lowercaseEnumVal +} diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.2.errors.txt b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.2.errors.txt new file mode 100644 index 0000000000..33360e96cf --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.2.errors.txt @@ -0,0 +1 @@ +5:5:Invalid property assignment: Enum value "lowercaseEnumVal" cannot start with a lowercase letter diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.2.qml b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.2.qml new file mode 100644 index 0000000000..0dfe26c71d --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseEnumCompileTime.2.qml @@ -0,0 +1,6 @@ +import QtQuick 2.0 +import Test 1.0 + +MyTypeObject { + enumProperty: MyTypeObjectSingleton.lowercaseEnumVal +} diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseEnumRuntime.1.qml b/tests/auto/qml/qqmllanguage/data/lowercaseEnumRuntime.1.qml new file mode 100644 index 0000000000..866b49e1d5 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseEnumRuntime.1.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import Test 1.0 + +MyTypeObject { + enumProperty: MyTypeObject.EnumVal1 + Component.onCompleted: { + var a = MyTypeObject.EnumVal1; + var b = MyTypeObject.lowercaseEnumVal + } +} diff --git a/tests/auto/qml/qqmllanguage/data/lowercaseEnumRuntime.2.qml b/tests/auto/qml/qqmllanguage/data/lowercaseEnumRuntime.2.qml new file mode 100644 index 0000000000..686977a11a --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lowercaseEnumRuntime.2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import Test 1.0 + +MyTypeObject { + intProperty: MyTypeObjectSingleton.EnumVal1 + Component.onCompleted: { + var a = MyTypeObjectSingleton.EnumVal1; + var b = MyTypeObjectSingleton.lowercaseEnumVal; + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index 4153aae1dc..cc39422dce 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -29,6 +29,14 @@ #include <private/qqmlcompiler_p.h> +static QObject *myTypeObjectSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + + return new MyTypeObject(); +} + void registerTypes() { qmlRegisterInterface<MyInterface>("MyInterface"); @@ -88,6 +96,10 @@ void registerTypes() qmlRegisterType<RootObjectInCreationTester>("Test", 1, 0, "RootObjectInCreationTester"); qmlRegisterType<MyCompositeBaseType>("Test", 1, 0, "MyCompositeBaseType"); + + qmlRegisterSingletonType<MyTypeObjectSingleton>("Test", 1, 0, "MyTypeObjectSingleton", myTypeObjectSingleton); + + qmlRegisterType<MyArrayBufferTestClass>("Test", 1, 0, "MyArrayBufferTestClass"); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 851ada9383..2344b6a03b 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -292,7 +292,7 @@ public: emit flagPropertyChanged(); } - enum MyEnum { EnumVal1, EnumVal2 }; + enum MyEnum { EnumVal1, EnumVal2, lowercaseEnumVal }; MyEnum enumPropertyValue; MyEnum enumProperty() const { return enumPropertyValue; @@ -592,6 +592,12 @@ signals: }; Q_DECLARE_OPERATORS_FOR_FLAGS(MyTypeObject::MyFlags) +// FIXME: If no subclass is used for the singleton registration with qmlRegisterSingletonType(), +// the valueTypes() test will fail. +class MyTypeObjectSingleton : public MyTypeObject +{ + Q_OBJECT +}; class MyContainer : public QObject { @@ -1084,6 +1090,58 @@ public: static QObject *qmlAttachedProperties(QObject *parent) { return new QObject(parent); } }; +class MyArrayBufferTestClass : public QObject +{ + Q_OBJECT + Q_PROPERTY(QByteArray byteArrayProperty READ byteArrayProperty WRITE setByteArrayProperty NOTIFY byteArrayPropertyChanged) + +signals: + void byteArrayPropertyChanged(); + void byteArraySignal(QByteArray arg); + +public: + QByteArray byteArrayPropertyValue; + QByteArray byteArrayProperty() const { + return byteArrayPropertyValue; + } + void setByteArrayProperty(const QByteArray &v) { + byteArrayPropertyValue = v; + emit byteArrayPropertyChanged(); + } + Q_INVOKABLE void emitByteArraySignal(char begin, char num) { + byteArraySignal(byteArrayMethod_CountUp(begin, num)); + } + Q_INVOKABLE int byteArrayMethod_Sum(QByteArray arg) { + int sum = 0; + for (int i = 0; i < arg.size(); ++i) { + sum += arg[i]; + } + return sum; + } + Q_INVOKABLE QByteArray byteArrayMethod_CountUp(char begin, int num) { + QByteArray ret; + for (int i = 0; i < num; ++i) { + ret.push_back(begin++); + } + return ret; + } + Q_INVOKABLE bool byteArrayMethod_Overloaded(QByteArray) { + return true; + } + Q_INVOKABLE bool byteArrayMethod_Overloaded(int) { + return false; + } + Q_INVOKABLE bool byteArrayMethod_Overloaded(QString) { + return false; + } + Q_INVOKABLE bool byteArrayMethod_Overloaded(QJSValue) { + return false; + } + Q_INVOKABLE bool byteArrayMethod_Overloaded(QVariant) { + return false; + } +}; + Q_DECLARE_METATYPE(MyEnum2Class::EnumB) Q_DECLARE_METATYPE(MyEnum1Class::EnumA) Q_DECLARE_METATYPE(Qt::TextFormat) diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 45507e83ea..dd7410dfd3 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -198,6 +198,10 @@ private slots: void crash2(); void globalEnums(); + void lowercaseEnumRuntime_data(); + void lowercaseEnumRuntime(); + void lowercaseEnumCompileTime_data(); + void lowercaseEnumCompileTime(); void literals_data(); void literals(); @@ -246,6 +250,9 @@ private slots: void deleteSingletons(); + void arrayBuffer_data(); + void arrayBuffer(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -3495,6 +3502,45 @@ void tst_qqmllanguage::globalEnums() delete o; } +void tst_qqmllanguage::lowercaseEnumRuntime_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QString>("errorMessage"); + + QTest::newRow("enum from normal type") << "lowercaseEnumRuntime.1.qml" << ":8: TypeError: Cannot access enum value 'lowercaseEnumVal' of 'MyTypeObject', enum values need to start with an uppercase letter."; + QTest::newRow("enum from singleton type") << "lowercaseEnumRuntime.2.qml" << ":8: TypeError: Cannot access enum value 'lowercaseEnumVal' of 'MyTypeObjectSingleton', enum values need to start with an uppercase letter."; +} + +void tst_qqmllanguage::lowercaseEnumRuntime() +{ + QFETCH(QString, file); + QFETCH(QString, errorMessage); + + QQmlComponent component(&engine, testFileUrl(file)); + VERIFY_ERRORS(0); + QString warning = component.url().toString() + errorMessage; + QTest::ignoreMessage(QtWarningMsg, qPrintable(warning)); + delete component.create(); +} + +void tst_qqmllanguage::lowercaseEnumCompileTime_data() +{ + QTest::addColumn<QString>("file"); + QTest::addColumn<QString>("errorFile"); + + QTest::newRow("assignment to int property") << "lowercaseEnumCompileTime.1.qml" << "lowercaseEnumCompileTime.1.errors.txt"; + QTest::newRow("assignment to enum property") << "lowercaseEnumCompileTime.2.qml" << "lowercaseEnumCompileTime.2.errors.txt"; +} + +void tst_qqmllanguage::lowercaseEnumCompileTime() +{ + QFETCH(QString, file); + QFETCH(QString, errorFile); + + QQmlComponent component(&engine, testFileUrl(file)); + VERIFY_ERRORS(qPrintable(errorFile)); +} + void tst_qqmllanguage::literals_data() { QTest::addColumn<QString>("property"); @@ -4102,6 +4148,27 @@ void tst_qqmllanguage::deleteSingletons() QVERIFY(singleton.data() == 0); } +void tst_qqmllanguage::arrayBuffer_data() +{ + QTest::addColumn<QString>("file"); + QTest::newRow("arraybuffer_property_get") << "arraybuffer_property_get.qml"; + QTest::newRow("arraybuffer_property_set") << "arraybuffer_property_set.qml"; + QTest::newRow("arraybuffer_signal_arg") << "arraybuffer_signal_arg.qml"; + QTest::newRow("arraybuffer_method_arg") << "arraybuffer_method_arg.qml"; + QTest::newRow("arraybuffer_method_return") << "arraybuffer_method_return.qml"; + QTest::newRow("arraybuffer_method_overload") << "arraybuffer_method_overload.qml"; +} + +void tst_qqmllanguage::arrayBuffer() +{ + QFETCH(QString, file); + QQmlComponent component(&engine, testFile(file)); + VERIFY_ERRORS(0); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(object->property("ok").toBool(), true); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 240e8a791e..548b20a80a 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -291,7 +291,6 @@ void tst_examples::sgsnippets_data() void tst_examples::sgsnippets() { - QQuickWindow window; QFETCH(QString, file); @@ -301,19 +300,26 @@ void tst_examples::sgsnippets() QCOMPARE(component.status(), QQmlComponent::Ready); QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QQuickWindow *window = qobject_cast<QQuickWindow*>(object.data()); QQuickItem *root = qobject_cast<QQuickItem *>(object.data()); - if (!root) + if (!root && !window) { component.completeCreate(); - QVERIFY(root); + QVERIFY(false); + } + if (!window) + window = new QQuickWindow; - window.resize(240, 320); - window.show(); - QVERIFY(QTest::qWaitForWindowExposed(&window)); + window->resize(240, 320); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); - root->setParentItem(window.contentItem()); + if (root) + root->setParentItem(window->contentItem()); component.completeCreate(); qApp->processEvents(); + if (root) + delete window; } QTEST_MAIN(tst_examples) diff --git a/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml b/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml new file mode 100644 index 0000000000..bfd475266e --- /dev/null +++ b/tests/auto/quick/qquickanimators/data/positionerWithAnimator.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 + +Column { + width: 200 + height: 200 + + property alias repeater: repeater + property alias transition: transition + + anchors.centerIn: parent + populate: Transition { + id: transition + ScaleAnimator { + from: 0 + to: 1 + } + } + + Repeater { + id: repeater + model: ["red", "green", "blue"] + + Rectangle { + width: 100 + height: 100 + color: modelData + scale: 0 + } + } +} diff --git a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp index 6943bf5045..29142dee12 100644 --- a/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp +++ b/tests/auto/quick/qquickanimators/tst_qquickanimators.cpp @@ -30,6 +30,8 @@ #include <QtQuick> #include <private/qquickanimator_p.h> +#include <private/qquickrepeater_p.h> +#include <private/qquicktransition_p.h> #include <QtQml> @@ -40,6 +42,7 @@ class tst_Animators: public QObject private slots: void testMultiWinAnimator_data(); void testMultiWinAnimator(); + void testTransitions(); }; void tst_Animators::testMultiWinAnimator_data() @@ -94,6 +97,28 @@ void tst_Animators::testMultiWinAnimator() QVERIFY(true); } +void tst_Animators::testTransitions() +{ + QQuickView view(QUrl::fromLocalFile("data/positionerWithAnimator.qml")); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(view.rootObject()); + + QQuickRepeater *repeater = view.rootObject()->property("repeater").value<QQuickRepeater *>(); + QVERIFY(repeater); + + QQuickItem *child = repeater->itemAt(0); + QVERIFY(child); + QCOMPARE(child->scale(), qreal(0.0)); + + QQuickTransition *transition = view.rootObject()->property("transition").value<QQuickTransition *>(); + QVERIFY(transition); + + QTRY_VERIFY(transition->running()); + QTRY_VERIFY(!transition->running()); + QCOMPARE(child->scale(), qreal(1.0)); +} + #include "tst_qquickanimators.moc" QTEST_MAIN(tst_Animators) diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp index 1bd163fc4a..114f906736 100644 --- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp +++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp @@ -47,6 +47,7 @@ private slots: void active(); void state(); void layoutDirection(); + void font(); void inputMethod(); void styleHints(); void cleanup(); @@ -196,6 +197,21 @@ void tst_qquickapplication::layoutDirection() QCOMPARE(Qt::LayoutDirection(item->property("layoutDirection").toInt()), Qt::LeftToRight); } +void tst_qquickapplication::font() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0; Item { property font defaultFont: Qt.application.font }", QUrl::fromLocalFile("")); + QQuickItem *item = qobject_cast<QQuickItem *>(component.create()); + QVERIFY(item); + QQuickView view; + item->setParentItem(view.rootObject()); + + QVariant defaultFontProperty = item->property("defaultFont"); + QVERIFY(defaultFontProperty.isValid()); + QCOMPARE(defaultFontProperty.type(), QVariant::Font); + QCOMPARE(defaultFontProperty.value<QFont>(), qApp->font()); +} + void tst_qquickapplication::inputMethod() { // technically not in QQuickApplication, but testing anyway here diff --git a/tests/auto/quick/qquickgridview/data/qtbug48870.qml b/tests/auto/quick/qquickgridview/data/qtbug48870.qml new file mode 100644 index 0000000000..7c0783fbb5 --- /dev/null +++ b/tests/auto/quick/qquickgridview/data/qtbug48870.qml @@ -0,0 +1,30 @@ +import QtQuick 2.6 + +Rectangle { + width: 500 + height: 500 + color: "blue" + + GridView { + id: view + objectName: "view" + anchors.fill: parent + model: testModel + cellWidth: 150 + cellHeight: 150 + readonly property int columns: Math.floor(width / cellWidth) + + delegate: Rectangle { + width: GridView.view.cellWidth + height: GridView.view.cellHeight + color: (row & 1) != (col & 1) ? "green" : "red" + readonly property int row: index / view.columns + readonly property int col: index % view.columns + + Text { + anchors.centerIn: parent + text: "Item " + index + } + } + } +} diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index b3474db7de..07c03a57d7 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -36,6 +36,7 @@ #include <QtQml/qqmlincubator.h> #include <QtQml/qqmlcontext.h> #include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemview_p_p.h> #include <QtQuick/private/qquickgridview_p.h> #include <QtQuick/private/qquicktext_p.h> #include <QtQml/private/qqmllistmodel_p.h> @@ -205,6 +206,7 @@ private slots: void contentHeightWithDelayRemove(); void QTBUG_45640(); + void QTBUG_48870_fastModelUpdates(); void keyNavigationEnabled(); @@ -6628,6 +6630,42 @@ void tst_QQuickGridView::keyNavigationEnabled() QCOMPARE(gridView->currentIndex(), 1); } +void tst_QQuickGridView::QTBUG_48870_fastModelUpdates() +{ + StressTestModel model; + + QScopedPointer<QQuickView> window(createView()); + QQmlContext *ctxt = window->rootContext(); + ctxt->setContextProperty("testModel", &model); + + window->setSource(testFileUrl("qtbug48870.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickGridView *view = findItem<QQuickGridView>(window->rootObject(), "view"); + QTRY_VERIFY(view != 0); + + QQuickItemViewPrivate *priv = QQuickItemViewPrivate::get(view); + bool nonUnique; + FxViewItem *item = Q_NULLPTR; + int expectedIdx; + QVERIFY(testVisibleItems(priv, &nonUnique, &item, &expectedIdx)); + + for (int i = 0; i < 10; i++) { + QTest::qWait(100); + QVERIFY2(testVisibleItems(priv, &nonUnique, &item, &expectedIdx), + qPrintable(!item ? QString("Unexpected null item") + : nonUnique ? QString("Non-unique item at %1 and %2").arg(item->index).arg(expectedIdx) + : QString("Found index %1, expected index is %3").arg(item->index).arg(expectedIdx))); + if (i % 3 != 0) { + if (i & 1) + flick(window.data(), QPoint(100, 200), QPoint(100, 0), 100); + else + flick(window.data(), QPoint(100, 200), QPoint(100, 400), 100); + } + } +} + QTEST_MAIN(tst_QQuickGridView) #include "tst_qquickgridview.moc" diff --git a/tests/auto/quick/qquickitem/data/childAtRectangle.qml b/tests/auto/quick/qquickitem/data/childAtRectangle.qml new file mode 100644 index 0000000000..d459c2b3f1 --- /dev/null +++ b/tests/auto/quick/qquickitem/data/childAtRectangle.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 20 + height: 20 + + Rectangle { + id: rect1 + + color: "red" + x: 0 + y: 0 + width: 16 + height: 16 + } + + Rectangle { + id: rect2 + + color: "red" + x: 18 + y: 0 + width: 1 + height: 1 + } + + Rectangle { + id: rect3 + + x: 19 + y: 19 + width: 0 + height: 0 + } + +} + diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 143f8f2b1c..4b2c86697e 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -167,6 +167,8 @@ private slots: void contains_data(); void contains(); + void childAt(); + private: enum PaintOrderOp { @@ -1966,6 +1968,46 @@ void tst_qquickitem::contains() QCOMPARE(result.toBool(), contains); } +void tst_qquickitem::childAt() +{ + QQuickView view; + view.setSource(testFileUrl("childAtRectangle.qml")); + QQuickItem *root = qobject_cast<QQuickItem*>(view.rootObject()); + + int found = 0; + for (int i = 0; i < 16; i++) + { + if (root->childAt(i, 0)) + found++; + } + QCOMPARE(found, 16); + + found = 0; + for (int i = 0; i < 16; i++) + { + if (root->childAt(0, i)) + found++; + } + QCOMPARE(found, 16); + + found = 0; + for (int i = 0; i < 2; i++) + { + if (root->childAt(18 + i, 0)) + found++; + } + QCOMPARE(found, 1); + + found = 0; + for (int i = 0; i < 16; i++) + { + if (root->childAt(18, i)) + found++; + } + QCOMPARE(found, 1); + + QVERIFY(!root->childAt(19,19)); +} QTEST_MAIN(tst_qquickitem) diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_1.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_1.qml new file mode 100644 index 0000000000..d8b7833467 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_1.qml @@ -0,0 +1,13 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item" + focus: true + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_2.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_2.qml new file mode 100644 index 0000000000..445dc6d2a1 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_2.qml @@ -0,0 +1,12 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_3.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_3.qml new file mode 100644 index 0000000000..806d48ebeb --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_3.qml @@ -0,0 +1,16 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item2" + focus: true + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_4.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_4.qml new file mode 100644 index 0000000000..6fcf513b51 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_4.qml @@ -0,0 +1,15 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + Item { + objectName: "item2" + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_5.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_5.qml new file mode 100644 index 0000000000..4f7b68a8de --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_5.qml @@ -0,0 +1,16 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + TextInput { + objectName: "item1" + activeFocusOnTab: true + } + Item { + objectName: "item2" + } +} diff --git a/tests/auto/quick/qquickitem2/data/qtbug_50516_2_6.qml b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_6.qml new file mode 100644 index 0000000000..223476327a --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/qtbug_50516_2_6.qml @@ -0,0 +1,17 @@ +import QtQuick 2.1 +import Test 1.0 + +TabFence2 { + objectName: "root" + focus: true + width: 800 + height: 600 + TextInput { + objectName: "item1" + activeFocusOnTab: true + } + TextInput { + objectName: "item2" + activeFocusOnTab: true + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index 607dbccaed..62ff09e698 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -70,6 +70,8 @@ private slots: void tabFence(); void qtbug_50516(); + void qtbug_50516_2_data(); + void qtbug_50516_2(); void keys(); void standardKeys_data(); @@ -305,6 +307,22 @@ public: QML_DECLARE_TYPE(TabFenceItem); +class TabFenceItem2 : public QQuickItem +{ + Q_OBJECT + +public: + TabFenceItem2(QQuickItem *parent = Q_NULLPTR) + : QQuickItem(parent) + { + QQuickItemPrivate *d = QQuickItemPrivate::get(this); + d->isTabFence = true; + setFlag(ItemIsFocusScope); + } +}; + +QML_DECLARE_TYPE(TabFenceItem2); + tst_QQuickItem::tst_QQuickItem() { } @@ -315,6 +333,7 @@ void tst_QQuickItem::initTestCase() qmlRegisterType<KeyTestItem>("Test",1,0,"KeyTestItem"); qmlRegisterType<HollowTestItem>("Test", 1, 0, "HollowTestItem"); qmlRegisterType<TabFenceItem>("Test", 1, 0, "TabFence"); + qmlRegisterType<TabFenceItem2>("Test", 1, 0, "TabFence2"); } void tst_QQuickItem::cleanup() @@ -1212,6 +1231,51 @@ void tst_QQuickItem::qtbug_50516() delete window; } +void tst_QQuickItem::qtbug_50516_2_data() +{ + QTest::addColumn<QString>("filename"); + QTest::addColumn<QString>("item1"); + QTest::addColumn<QString>("item2"); + + QTest::newRow("FocusScope TabFence with one Item(focused)") + << QStringLiteral("qtbug_50516_2_1.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with one Item(unfocused)") + << QStringLiteral("qtbug_50516_2_2.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with two Items(focused)") + << QStringLiteral("qtbug_50516_2_3.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with two Items(unfocused)") + << QStringLiteral("qtbug_50516_2_4.qml") << QStringLiteral("root") << QStringLiteral("root"); + QTest::newRow("FocusScope TabFence with one Item and one TextInput(unfocused)") + << QStringLiteral("qtbug_50516_2_5.qml") << QStringLiteral("item1") << QStringLiteral("item1"); + QTest::newRow("FocusScope TabFence with two TextInputs(unfocused)") + << QStringLiteral("qtbug_50516_2_6.qml") << QStringLiteral("item1") << QStringLiteral("item2"); +} + +void tst_QQuickItem::qtbug_50516_2() +{ + QFETCH(QString, filename); + QFETCH(QString, item1); + QFETCH(QString, item2); + + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl(filename)); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + QVERIFY(window->rootObject()->hasActiveFocus()); + + QQuickItem *contentItem = window->rootObject(); + QQuickItem *next = contentItem->nextItemInFocusChain(true); + QCOMPARE(next->objectName(), item1); + next = contentItem->nextItemInFocusChain(false); + QCOMPARE(next->objectName(), item2); + + delete window; +} + void tst_QQuickItem::keys() { QQuickView *window = new QQuickView(0); @@ -2931,12 +2995,12 @@ void tst_QQuickItem::childAt() child3.setParentItem(&parent); QCOMPARE(parent.childAt(0, 0), &child1); - QCOMPARE(parent.childAt(0, 100), &child1); + QCOMPARE(parent.childAt(0, 99), &child1); QCOMPARE(parent.childAt(25, 25), &child1); QCOMPARE(parent.childAt(25, 75), &child1); QCOMPARE(parent.childAt(75, 25), &child1); QCOMPARE(parent.childAt(75, 75), &child2); - QCOMPARE(parent.childAt(150, 150), &child2); + QCOMPARE(parent.childAt(149, 149), &child2); QCOMPARE(parent.childAt(25, 200), &child3); QCOMPARE(parent.childAt(0, 150), static_cast<QQuickItem *>(0)); QCOMPARE(parent.childAt(300, 300), static_cast<QQuickItem *>(0)); diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml new file mode 100644 index 0000000000..6a1c0632ad --- /dev/null +++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml @@ -0,0 +1,1036 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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.2 +import QtTest 1.0 +import QtQuick.Layouts 1.1 + +Item { + id: container + width: 200 + height: 200 + TestCase { + id: testCase + name: "Tests_GridLayout" + when: windowShown + width: 200 + height: 200 + + Component { + id: layout_flow_Component + GridLayout { + columns: 4 + columnSpacing: 0 + rowSpacing: 0 + Repeater { + model: 6 + Rectangle { + property var itemRect: [x, y, width, height] + color: "red" + Layout.preferredWidth: 10 + Layout.preferredHeight: 10 + Text { text: index } + } + } + } + } + + function test_flow() + { + var layout = layout_flow_Component.createObject(container); + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10]) + tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10]) + tryCompare(layout.children[3], "itemRect", [30, 0, 10, 10]) + + tryCompare(layout.children[4], "itemRect", [ 0, 10, 10, 10]) + tryCompare(layout.children[5], "itemRect", [10, 10, 10, 10]) + + layout.rows = 4 + layout.flow = GridLayout.TopToBottom + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [ 0, 10, 10, 10]) + tryCompare(layout.children[2], "itemRect", [ 0, 20, 10, 10]) + tryCompare(layout.children[3], "itemRect", [ 0, 30, 10, 10]) + + tryCompare(layout.children[4], "itemRect", [10, 0, 10, 10]) + tryCompare(layout.children[5], "itemRect", [10, 10, 10, 10]) + + layout.destroy() + } + + Component { + id: layout_flowLeftToRight_Component + GridLayout { + columns: 4 + columnSpacing: 0 + rowSpacing: 0 + // red rectangles are auto-positioned + // black rectangles are explicitly positioned with row,column + Rectangle { + // First one should auto position itself at (0,0) + id: r1 + color: "red" + width: 20 + height: 20 + } + Rectangle { + // (1,1) + id: r2 + color: "black" + width: 20 + height: 20 + Layout.row: 1 + Layout.column: 1 + Layout.rowSpan: 2 + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + } + Rectangle { + // (0,1) + id: r3 + color: "black" + width: 20 + height: 20 + Layout.row: 0 + Layout.column: 1 + } + Rectangle { + // This one won't fit on the left and right sides of the big black box + // inserted at (3,0) + id: r4 + color: "red" + width: 20 + height: 20 + Layout.columnSpan: 2 + Layout.rowSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + } + Rectangle { + // continue flow from (0,2) + id: r5 + color: "black" + width: 20 + height: 20 + Layout.row: 0 + Layout.column: 2 + } + Repeater { + // ...and let the rest of the items automatically fill in the empty cells + model: 8 + Rectangle { + color: "red" + width: 20 + height: 20 + Text { text: index } + } + } + } + } + + function test_flowLeftToRight() { + var layout = layout_flowLeftToRight_Component.createObject(container); + compare(layout.implicitWidth, 80); + compare(layout.children[0].x, 0); + compare(layout.children[0].y, 0); + compare(layout.children[1].x, 20); + compare(layout.children[1].y, 20); + compare(layout.children[2].x, 20); + compare(layout.children[2].y, 0); + compare(layout.children[3].x, 0); + compare(layout.children[3].y, 60); + compare(layout.children[4].x, 40); + compare(layout.children[4].y, 0); + + // assumes that the repeater is the last item among the items it creates + compare(layout.children[5].x, 60); + compare(layout.children[5].y, 00); + compare(layout.children[6].x, 00); + compare(layout.children[6].y, 20); + compare(layout.children[7].x, 60); + compare(layout.children[7].y, 20); + compare(layout.children[8].x, 00); + compare(layout.children[8].y, 40); + compare(layout.children[9].x, 60); + compare(layout.children[9].y, 40); + compare(layout.children[10].x, 40); + compare(layout.children[10].y, 60); + compare(layout.children[11].x, 60); + compare(layout.children[11].y, 60); + compare(layout.children[12].x, 40); + compare(layout.children[12].y, 80); + + layout.destroy(); + } + + + Component { + id: layout_flowLeftToRightDefaultPositions_Component + GridLayout { + columns: 2 + columnSpacing: 0 + rowSpacing: 0 + // red rectangles are auto-positioned + // black rectangles are explicitly positioned with row,column + // gray rectangles are items with just one row or just one column specified + Rectangle { + // First one should auto position itself at (0,0) + id: r1 + color: "red" + width: 20 + height: 20 + } + Rectangle { + // (1,0) + id: r2 + color: "gray" + width: 20 + height: 20 + Layout.row: 1 + } + Rectangle { + // (1,1) + id: r3 + color: "black" + width: 20 + height: 20 + Layout.row: 1 + Layout.column: 1 + } + Rectangle { + // (1,0), warning emitted + id: r4 + color: "gray" + width: 20 + height: 20 + Layout.row: 1 + } + } + } + + function test_flowLeftToRightDefaultPositions() { + ignoreWarning("QGridLayoutEngine::addItem: Cell (1, 0) already taken"); + var layout = layout_flowLeftToRightDefaultPositions_Component.createObject(container); + compare(layout.implicitWidth, 40); + compare(layout.children[0].x, 0); + compare(layout.children[0].y, 0); + compare(layout.children[1].x, 0); + compare(layout.children[1].y, 20); + compare(layout.children[2].x, 20); + compare(layout.children[2].y, 20); + layout.destroy(); + } + + + Component { + id: layout_flowTopToBottom_Component + GridLayout { + rows: 4 + columnSpacing: 0 + rowSpacing: 0 + flow: GridLayout.TopToBottom + // red rectangles are auto-positioned + // black rectangles are explicitly positioned with row,column + Rectangle { + // First one should auto position itself at (0,0) + id: r1 + color: "red" + width: 20 + height: 20 + } + Rectangle { + // (1,1) + id: r2 + color: "black" + width: 20 + height: 20 + Layout.row: 1 + Layout.column: 1 + Layout.rowSpan: 2 + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + } + Rectangle { + // (2,0) + id: r3 + color: "black" + width: 20 + height: 20 + Layout.row: 2 + Layout.column: 0 + } + Rectangle { + // This one won't fit on the left and right sides of the big black box + // inserted at (0,3) + id: r4 + color: "red" + width: 20 + height: 20 + Layout.rowSpan: 2 + Layout.fillHeight: true + } + Rectangle { + // continue flow from (1,0) + id: r5 + color: "black" + width: 20 + height: 20 + Layout.row: 1 + Layout.column: 0 + } + Repeater { + // ...and let the rest of the items automatically fill in the empty cells + model: 8 + Rectangle { + color: "red" + width: 20 + height: 20 + Text { text: index } + } + } + } + } + + function test_flowTopToBottom() { + var layout = layout_flowTopToBottom_Component.createObject(container); + compare(layout.children[0].x, 0); + compare(layout.children[0].y, 0); + compare(layout.children[1].x, 20); + compare(layout.children[1].y, 20); + compare(layout.children[2].x, 0); + compare(layout.children[2].y, 40); + compare(layout.children[3].x, 60); + compare(layout.children[3].y, 0); + compare(layout.children[4].x, 0); + compare(layout.children[4].y, 20); + + // The repeated items + compare(layout.children[5].x, 0); + compare(layout.children[5].y, 60); + compare(layout.children[6].x, 20); + compare(layout.children[6].y, 0); + compare(layout.children[7].x, 20); + compare(layout.children[7].y, 60); + compare(layout.children[8].x, 40); + compare(layout.children[8].y, 0); + compare(layout.children[9].x, 40); + compare(layout.children[9].y, 60); + compare(layout.children[10].x, 60); + compare(layout.children[10].y, 40); + compare(layout.children[11].x, 60); + compare(layout.children[11].y, 60); + compare(layout.children[12].x, 80); + compare(layout.children[12].y, 0); + + layout.destroy(); + } + + Component { + id: layout_spanAcrossEmptyRows_Component + /* This test has a large number of empty rows and columns, but there is one item + that spans across some of these empty rows/columns. + Do not modify (especially do not add items unless you understand what this is + testing) + */ + + GridLayout { + columnSpacing: 0 + rowSpacing: 0 + // black rectangles are explicitly positioned with row,column + Rectangle { + // (0,0) + id: r0 + color: "black" + Layout.row: 0 + Layout.column: 0 + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.maximumWidth: 40 + Layout.maximumHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + Rectangle { + // (0,1) + id: r1 + color: "black" + Layout.row: 0 + Layout.column: 1 + Layout.columnSpan: 2 + Layout.rowSpan: 2 + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.maximumWidth: 40 + Layout.maximumHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + Rectangle { + // (0,99) + id: r2 + color: "black" + Layout.row: 0 + Layout.column: 99 + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.maximumWidth: 40 + Layout.maximumHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + function test_spanAcrossEmptyRows() { + var layout = layout_spanAcrossEmptyRows_Component.createObject(container); + compare(layout.children[0].x, 0); + compare(layout.children[0].y, 0); + compare(layout.children[1].x, 20); + compare(layout.children[1].y, 0); + compare(layout.children[2].x, 40); + compare(layout.children[2].y, 0); + + compare(layout.implicitWidth, 60); + compare(layout.Layout.maximumWidth, 120); + + layout.destroy(); + } + + Component { + id: layout_spanIsMoreThanColumns_Component + + GridLayout { + columnSpacing: 1 + rowSpacing: 1 + columns: 2 + + Rectangle { + implicitWidth: 10 + implicitHeight: 10 + Layout.columnSpan: 3 + } + } + } + + function test_spanIsMoreThanColumns() { + var layout = layout_spanIsMoreThanColumns_Component.createObject(container); + // item was not added, therefore implicit width is 0 + compare(layout.implicitWidth, 0); + layout.destroy(); + } + + function test_sizeHints() { + var layout = layout_spanAcrossEmptyRows_Component.createObject(container); + compare(layout.visible, true) + + var minWidth = layout.Layout.minimumWidth + var minHeight = layout.Layout.minimumHeight + + var prefWidth = layout.implicitWidth + var prefHeight = layout.implicitHeight + + var maxWidth = layout.Layout.maximumWidth + var maxHeight = layout.Layout.maximumHeight + + layout.visible = false + compare(minWidth, layout.Layout.minimumWidth) + compare(minHeight, layout.Layout.minimumHeight) + compare(prefWidth, layout.implicitWidth) + compare(prefHeight, layout.implicitHeight) + compare(maxWidth, layout.Layout.maximumWidth) + compare(maxHeight, layout.Layout.maximumHeight) + + layout.destroy(); + } + + Component { + id: layout_alignment_Component + GridLayout { + columns: 2 + columnSpacing: 0 + rowSpacing: 0 + Rectangle { + // First one should auto position itself at (0,0) + property var itemRect: [x, y, width, height] + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.fillWidth: true + Layout.fillHeight: true + } + Rectangle { + // (0,1) + property var itemRect: [x, y, width, height] + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.alignment: Qt.AlignBottom + } + Rectangle { + // (1,0) + property var itemRect: [x, y, width, height] + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.alignment: Qt.AlignRight + } + Rectangle { + // (1,1) + property var itemRect: [x, y, width, height] + color: "red" + Layout.preferredWidth: 10 + Layout.preferredHeight: 10 + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + } + Rectangle { + // (2,0) + property var itemRect: [x, y, width, height] + color: "red" + Layout.preferredWidth: 30 + Layout.preferredHeight: 30 + Layout.alignment: Qt.AlignRight + Layout.columnSpan: 2 + } + Rectangle { + // (3,0) + property var itemRect: [x, y, width, height] + baselineOffset: 7 + color: "red" + Layout.row: 3 + Layout.column: 0 + Layout.preferredWidth: 10 + Layout.preferredHeight: 10 + Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline + } + Rectangle { + // (3,1) + property var itemRect: [x, y, width, height] + baselineOffset: 7 + color: "red" + Layout.preferredWidth: 10 + Layout.preferredHeight: 10 + Layout.alignment: Qt.AlignRight | Qt.AlignBaseline + } + + } + } + + function test_alignment() + { + var layout = layout_alignment_Component.createObject(container); + layout.width = 60; + layout.height = 100; + + + tryCompare(layout.children[0], "itemRect", [ 0, 0, 40, 40]); + tryCompare(layout.children[1], "itemRect", [40, 20, 20, 20]); + tryCompare(layout.children[2], "itemRect", [20, 40, 20, 20]); + tryCompare(layout.children[3], "itemRect", [45, 40, 10, 10]); + tryCompare(layout.children[4], "itemRect", [30, 60, 30, 30]); + tryCompare(layout.children[5], "itemRect", [ 0, 90, 10, 10]); + tryCompare(layout.children[6], "itemRect", [50, 90, 10, 10]); + + + layout.children[1].Layout.alignment = Qt.AlignTop + tryCompare(layout.children[1], "x", 40); + tryCompare(layout.children[1], "y", 0); + + layout.children[2].Layout.alignment = Qt.AlignLeft + tryCompare(layout.children[2], "x", 0); + tryCompare(layout.children[2], "y", 40); + + layout.children[3].Layout.alignment = Qt.AlignLeft|Qt.AlignVCenter + tryCompare(layout.children[3], "x", 40); + tryCompare(layout.children[3], "y", 45); + + layout.children[4].Layout.alignment = Qt.AlignLeft + tryCompare(layout.children[4], "x", 0); + tryCompare(layout.children[4], "y", 60); + + layout.destroy(); + } + + + Component { + id: layout_rightToLeft_Component + GridLayout { + layoutDirection: Qt.RightToLeft + columnSpacing: 0 + rowSpacing: 0 + columns: 3 + Rectangle { + property var itemRect: [x, y, width, height] + color: "#cbffc4" + Layout.preferredWidth: 50 + Layout.preferredHeight: 50 + Layout.alignment: Qt.AlignCenter + } + Rectangle { + property var itemRect: [x, y, width, height] + color: "#c4d1ff" + Layout.preferredWidth: 50 + Layout.preferredHeight: 50 + Layout.alignment: Qt.AlignRight + } + Rectangle { + property var itemRect: [x, y, width, height] + color: "#ffd5c4" + Layout.preferredWidth: 50 + Layout.preferredHeight: 50 + Layout.alignment: Qt.AlignLeft + } + } + } + + function verifyIsRightToLeft(layout) + { + tryCompare(layout.children[0], "itemRect", [125, 0, 50, 50]); + tryCompare(layout.children[1], "itemRect", [60, 0, 50, 50]); + tryCompare(layout.children[2], "itemRect", [10, 0, 50, 50]); + } + + function verifyIsLeftToRight(layout) + { + tryCompare(layout.children[0], "itemRect", [5, 0, 50, 50]); + tryCompare(layout.children[1], "itemRect", [70, 0, 50, 50]); + tryCompare(layout.children[2], "itemRect", [120, 0, 50, 50]); + } + + function test_rightToLeft() + { + var layout = layout_rightToLeft_Component.createObject(container); + layout.width = 180; + layout.height = 50; + + // Right To Left + verifyIsRightToLeft(layout) + layout.LayoutMirroring.enabled = true + layout.layoutDirection = Qt.LeftToRight + verifyIsRightToLeft(layout) + + // Left To Right + layout.LayoutMirroring.enabled = false + layout.layoutDirection = Qt.LeftToRight + verifyIsLeftToRight(layout); + layout.LayoutMirroring.enabled = true + layout.layoutDirection = Qt.RightToLeft + verifyIsLeftToRight(layout); + + layout.LayoutMirroring.enabled = false + verifyIsRightToLeft(layout) + + layout.layoutDirection = Qt.LeftToRight + verifyIsLeftToRight(layout); + + layout.LayoutMirroring.enabled = true + verifyIsRightToLeft(layout) + + layout.destroy(); + } + + Component { + id: layout_columnsOrRowsChanged_Component + GridLayout { + id: layout + rowSpacing: 0 + columnSpacing: 0 + Repeater { + model: 4 + Rectangle { + property var itemRect: [x, y, width, height] + width: 10 + height: 10 + color: "#ff0000" + } + } + } + } + + function test_columnsChanged() + { + var layout = layout_columnsOrRowsChanged_Component.createObject(container); + layout.width = 40; + layout.height = 20; + tryCompare(layout.children[0], "itemRect", [ 0, 5, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10, 5, 10, 10]) + tryCompare(layout.children[2], "itemRect", [20, 5, 10, 10]) + tryCompare(layout.children[3], "itemRect", [30, 5, 10, 10]) + + layout.columns = 2 + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [20, 0, 10, 10]) + tryCompare(layout.children[2], "itemRect", [ 0, 10, 10, 10]) + tryCompare(layout.children[3], "itemRect", [20, 10, 10, 10]) + + layout.destroy() + } + + function test_rowsChanged() + { + var layout = layout_columnsOrRowsChanged_Component.createObject(container); + layout.flow = GridLayout.TopToBottom + layout.width = 20; + layout.height = 40; + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [ 0, 10, 10, 10]) + tryCompare(layout.children[2], "itemRect", [ 0, 20, 10, 10]) + tryCompare(layout.children[3], "itemRect", [ 0, 30, 10, 10]) + + layout.rows = 2 + tryCompare(layout.children[0], "itemRect", [ 0, 5, 10, 10]) + tryCompare(layout.children[1], "itemRect", [ 0, 25, 10, 10]) + tryCompare(layout.children[2], "itemRect", [10, 5, 10, 10]) + tryCompare(layout.children[3], "itemRect", [10, 25, 10, 10]) + + layout.destroy() + } + + Component { + id: layout_columnOrRowChanged_Component + GridLayout { + id: layout + rowSpacing: 0 + columnSpacing: 0 + Rectangle { + property var itemRect: [x, y, width, height] + width: 10 + height: 10 + Layout.column: 0 + color: "#ff0000" + } + Rectangle { + property var itemRect: [x, y, width, height] + Layout.column: 1 + width: 10 + height: 10 + color: "#ff0000" + } + Rectangle { + property var itemRect: [x, y, width, height] + //Layout.column: 2 + width: 10 + height: 10 + color: "#ff0000" + } + } + } + + function test_columnOrRowChanged() + { + var layout = layout_columnOrRowChanged_Component.createObject(container); + layout.width = layout.implicitWidth + layout.height = layout.implicitHeight + // c0-c1-c2 + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10]) + tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10]) + + layout.children[0].Layout.column = 3 + //c1-c2-c0 + tryCompare(layout.children[0], "itemRect", [20, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[2], "itemRect", [10, 0, 10, 10]) + + layout.children[2].Layout.column = 4 + //c1-c0-c2 + tryCompare(layout.children[0], "itemRect", [10, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10]) + + layout.children[0].Layout.row = 1 + // two rows, so we adjust it to its new implicitHeight + layout.height = layout.implicitHeight + //c1 c2 + // c0 + tryCompare(layout.children[0], "itemRect", [10, 10, 10, 10]) + tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10]) + + layout.destroy() + } + + Component { + id: layout_baselines_Component + GridLayout { + id: layout + columnSpacing: 0 + Rectangle { + property var itemRect: [x, y, width, height] + implicitWidth: 10 + implicitHeight: 10 + baselineOffset: 10 + } + Rectangle { + property var itemRect: [x, y, width, height] + implicitWidth: 10 + implicitHeight: 10 + } + } + } + function test_baselines() + { + var layout = layout_baselines_Component.createObject(container); + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10]) + compare(layout.implicitWidth, 20) + compare(layout.implicitHeight, 10) + + layout.children[0].Layout.alignment = Qt.AlignBaseline + layout.children[1].Layout.alignment = Qt.AlignBaseline + + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10, 10, 10, 10]) + compare(layout.implicitWidth, 20) + compare(layout.implicitHeight, 20) + + layout.destroy(); + } + + Component { + id: layout_spacings_Component + GridLayout { + id: layout + Repeater { + model: 2 + Rectangle { + property var itemRect: [x, y, width, height] + implicitWidth: 10 + implicitHeight: 10 + } + } + } + } + + function test_spacings() + { + var layout = layout_spacings_Component.createObject(container); + + // breaks down below -19. This is acceptable, since it means that the implicit size of the layout is negative + var testSpacings = [Number.NaN, 0, 10, -5, -19] + layout.rowSpacing = 0 + for (var i = 0; i < testSpacings.length; ++i) { + var sp = testSpacings[i] + if (isNaN(sp)) { + sp = 5 // Test defaults + } else { + layout.columnSpacing = sp + } + tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10]) + tryCompare(layout.children[1], "itemRect", [10 + sp, 0, 10, 10]) + compare(layout.implicitWidth, 20 + sp) + } + + // do not crash + layout.columnSpacing = -100 + waitForRendering(layout) + verify(isFinite(layout.implicitWidth)) + layout.destroy(); + } + + Component { + id: layout_alignToPixelGrid_Component + GridLayout { + columns: 3 + rowSpacing: 0 + columnSpacing: 2 + Repeater { + model: 3*3 + Rectangle { + color: "red" + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + function test_alignToPixelGrid() + { + var layout = layout_alignToPixelGrid_Component.createObject(container) + layout.width = 30 + layout.height = 28 + + var rectWidth = (layout.width - 2 * layout.columnSpacing)/3 + var rectHeight = layout.height/3 + + waitForRendering(layout); + + var sp = layout.columnSpacing + var idealGeom = [0,0,rectWidth,rectHeight] + for (var r = 0; r < 3; ++r) { + idealGeom[0] = 0 + idealGeom[2] = rectWidth + for (var c = 0; c < 3; ++c) { + var child = layout.children[3*r + c] + var visualGeom = [child.x, child.y, child.x + child.width, child.y + child.height] + + // verify that visualGeom is an integer number + for (var i = 0; i < 2; ++i) + compare(visualGeom[i] % 1, 0) + + // verify that x,y is no more than one pixel from idealGeom + fuzzyCompare(visualGeom[0], idealGeom[0], 1) + fuzzyCompare(visualGeom[1], idealGeom[1], 1) + + // verify that the visual size is no more than 1 pixel taller/wider than the ideal size. + verify(visualGeom[2] <= idealGeom[2] + 1) + verify(visualGeom[3] <= idealGeom[3] + 1) + idealGeom[0] = idealGeom[2] + sp + idealGeom[2] = idealGeom[0] + rectWidth + } + idealGeom[1] = idealGeom[3] + idealGeom[3] = idealGeom[1] + rectHeight + } + + layout.destroy() + } + + Component { + + id: layout_Margins_Component + GridLayout { + columns: 2 + rowSpacing: 0 + columnSpacing: 0 + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.margins: 10 + Layout.leftMargin: 2 + Layout.topMargin: 3 + Layout.rightMargin: 4 + Layout.bottomMargin: 4 + } + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.leftMargin: 4 + Layout.topMargin: 5 + Layout.rightMargin: 6 + Layout.bottomMargin: 6 + } + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.leftMargin: 3 + Layout.topMargin: 4 + Layout.rightMargin: 5 + Layout.bottomMargin: 5 + } + } + } + + function test_Margins() + { + var layout = layout_Margins_Component.createObject(container) + + compare(layout.implicitWidth, 3 + 20 + 5 + 4 + 20 + 6) + compare(layout.implicitHeight, 5 + 20 + 6 + 4 + 20 + 5) + layout.width = layout.implicitWidth + layout.height = layout.implicitHeight + + waitForRendering(layout) + + var c0 = layout.children[0] + var c1 = layout.children[1] + var c2 = layout.children[2] + + compare(c0.x, 2) + compare(c0.y, 5) + compare(c1.x, 3 + 20 + 5 + 4) + compare(c1.y, 5) + compare(c2.x, 3) + compare(c2.y, 5 + 20 + 6 + 4) + + // reset left|rightMargin. It should then use the generic "margins" property + c0.Layout.leftMargin = undefined + compare(layout.implicitWidth, 10 + 20 + 4 + 4 + 20 + 6) + c0.Layout.bottomMargin = undefined + compare(layout.implicitHeight, 3 + 20 + 10 + 4 + 20 + 5) + } + + Component { + id: layout_invalidateWhileRearranging_Component + + GridLayout { + columns: 1 + Rectangle { + height: 50 + Layout.fillWidth: true + color: 'blue' + } + + Rectangle { + height: 50 + Layout.fillWidth: true + color: 'red' + onYChanged: { + visible = false; + } + } + } + } + + function test_invalidateWhileRearranging_QTBUG_44139() + { + var layout = layout_invalidateWhileRearranging_Component.createObject(container) + + waitForRendering(layout); + verify(layout.children[1].visible == false); + layout.destroy() + } + } +} diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml new file mode 100644 index 0000000000..4b47b396a3 --- /dev/null +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -0,0 +1,921 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, 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 The Qt Company Ltd 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.2 +import QtTest 1.0 +import QtQuick.Layouts 1.0 + +Item { + id: container + width: 200 + height: 200 + TestCase { + id: testCase + name: "Tests_RowLayout" + when: windowShown + width: 200 + height: 200 + + function itemRect(item) + { + return [item.x, item.y, item.width, item.height]; + } + + function test_fixedAndExpanding() { + var test_layoutStr = + 'import QtQuick 2.2; \ + import QtQuick.Layouts 1.0; \ + RowLayout { \ + id: row; \ + width: 15; \ + spacing: 0; \ + property alias r1: _r1; \ + Rectangle { \ + id: _r1; \ + width: 5; \ + height: 10; \ + color: "#8080ff"; \ + Layout.fillWidth: false \ + } \ + property alias r2: _r2; \ + Rectangle { \ + id: _r2; \ + width: 10; \ + height: 20; \ + color: "#c0c0ff"; \ + Layout.fillWidth: true \ + } \ + } ' + + var lay = Qt.createQmlObject(test_layoutStr, container, ''); + tryCompare(lay, 'implicitWidth', 15); + compare(lay.implicitHeight, 20); + compare(lay.height, 20); + lay.width = 30 + compare(lay.r1.x, 0); + compare(lay.r1.width, 5); + compare(lay.r2.x, 5); + compare(lay.r2.width, 25); + lay.destroy() + } + + function test_allExpanding() { + var test_layoutStr = + 'import QtQuick 2.2; \ + import QtQuick.Layouts 1.0; \ + RowLayout { \ + id: row; \ + width: 15; \ + spacing: 0; \ + property alias r1: _r1; \ + Rectangle { \ + id: _r1; \ + width: 5; \ + height: 10; \ + color: "#8080ff"; \ + Layout.fillWidth: true \ + } \ + property alias r2: _r2; \ + Rectangle { \ + id: _r2; \ + width: 10; \ + height: 20; \ + color: "#c0c0ff"; \ + Layout.fillWidth: true \ + } \ + } ' + + var tmp = Qt.createQmlObject(test_layoutStr, container, ''); + tryCompare(tmp, 'implicitWidth', 15); + compare(tmp.implicitHeight, 20); + compare(tmp.height, 20); + tmp.width = 30 + compare(tmp.r1.width, 10); + compare(tmp.r2.width, 20); + compare(tmp.Layout.minimumWidth, 0) + compare(tmp.Layout.maximumWidth, Number.POSITIVE_INFINITY) + tmp.destroy() + } + + function test_initialNestedLayouts() { + var test_layoutStr = + 'import QtQuick 2.2; \ + import QtQuick.Layouts 1.0; \ + ColumnLayout { \ + id : col; \ + property alias row: _row; \ + objectName: "col"; \ + anchors.fill: parent; \ + RowLayout { \ + id : _row; \ + property alias r1: _r1; \ + property alias r2: _r2; \ + objectName: "row"; \ + spacing: 0; \ + Rectangle { \ + id: _r1; \ + color: "red"; \ + implicitWidth: 50; \ + implicitHeight: 20; \ + } \ + Rectangle { \ + id: _r2; \ + color: "green"; \ + implicitWidth: 50; \ + implicitHeight: 20; \ + Layout.fillWidth: true; \ + } \ + } \ + } ' + var col = Qt.createQmlObject(test_layoutStr, container, ''); + tryCompare(col, 'width', 200); + tryCompare(col.row, 'width', 200); + tryCompare(col.row.r1, 'width', 50); + tryCompare(col.row.r2, 'width', 150); + col.destroy() + } + + function test_implicitSize() { + var test_layoutStr = + 'import QtQuick 2.2; \ + import QtQuick.Layouts 1.0; \ + RowLayout { \ + id: row; \ + objectName: "row"; \ + spacing: 0; \ + height: 30; \ + anchors.left: parent.left; \ + anchors.right: parent.right; \ + Rectangle { \ + color: "red"; \ + height: 2; \ + Layout.minimumWidth: 50; \ + } \ + Rectangle { \ + color: "green"; \ + width: 10; \ + Layout.minimumHeight: 4; \ + } \ + Rectangle { \ + implicitWidth: 1000; \ + Layout.maximumWidth: 40; \ + implicitHeight: 6 \ + } \ + } ' + var row = Qt.createQmlObject(test_layoutStr, container, ''); + compare(row.implicitWidth, 50 + 10 + 40); + compare(row.implicitHeight, 6); + row.destroy() + } + + function test_countGeometryChanges() { + var test_layoutStr = + 'import QtQuick 2.2; \ + import QtQuick.Layouts 1.0; \ + ColumnLayout { \ + id : col; \ + property alias row: _row; \ + objectName: "col"; \ + anchors.fill: parent; \ + RowLayout { \ + id : _row; \ + property alias r1: _r1; \ + property alias r2: _r2; \ + objectName: "row"; \ + spacing: 0; \ + property int counter : 0; \ + onWidthChanged: { ++counter; } \ + Rectangle { \ + id: _r1; \ + color: "red"; \ + implicitWidth: 50; \ + implicitHeight: 20; \ + property int counter : 0; \ + onWidthChanged: { ++counter; } \ + Layout.fillWidth: true; \ + } \ + Rectangle { \ + id: _r2; \ + color: "green"; \ + implicitWidth: 50; \ + implicitHeight: 20; \ + property int counter : 0; \ + onWidthChanged: { ++counter; } \ + Layout.fillWidth: true; \ + } \ + } \ + } ' + var col = Qt.createQmlObject(test_layoutStr, container, ''); + compare(col.width, 200); + compare(col.row.width, 200); + compare(col.row.r1.width, 100); + compare(col.row.r2.width, 100); + compare(col.row.r1.counter, 1); + compare(col.row.r2.counter, 1); + verify(col.row.counter <= 2); + col.destroy() + } + + Component { + id: layoutItem_Component + Rectangle { + implicitWidth: 20 + implicitHeight: 20 + } + } + + Component { + id: columnLayoutItem_Component + ColumnLayout { + spacing: 0 + } + } + + Component { + id: layout_addAndRemoveItems_Component + RowLayout { + spacing: 0 + } + } + + function test_addAndRemoveItems() + { + var layout = layout_addAndRemoveItems_Component.createObject(container) + compare(layout.implicitWidth, 0) + compare(layout.implicitHeight, 0) + + var rect0 = layoutItem_Component.createObject(layout) + compare(layout.implicitWidth, 20) + compare(layout.implicitHeight, 20) + + var rect1 = layoutItem_Component.createObject(layout) + rect1.Layout.preferredWidth = 30; + rect1.Layout.preferredHeight = 30; + compare(layout.implicitWidth, 50) + compare(layout.implicitHeight, 30) + + var col = columnLayoutItem_Component.createObject(layout) + var rect2 = layoutItem_Component.createObject(col) + rect2.Layout.fillHeight = true + var rect3 = layoutItem_Component.createObject(col) + rect3.Layout.fillHeight = true + + compare(layout.implicitWidth, 70) + compare(col.implicitHeight, 40) + compare(layout.implicitHeight, 40) + + rect3.destroy() + wait(0) // this will hopefully effectuate the destruction of the object + + col.destroy() + wait(0) + compare(layout.implicitWidth, 50) + compare(layout.implicitHeight, 30) + + rect0.destroy() + wait(0) + compare(layout.implicitWidth, 30) + compare(layout.implicitHeight, 30) + + rect1.destroy() + wait(0) + compare(layout.implicitWidth, 0) + compare(layout.implicitHeight, 0) + + layout.destroy() + } + + Component { + id: layout_alignment_Component + RowLayout { + spacing: 0 + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.fillHeight: true + } + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + // use default alignment + } + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.alignment: Qt.AlignTop + } + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.alignment: Qt.AlignVCenter + } + Rectangle { + color: "red" + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.alignment: Qt.AlignBottom + } + } + } + + function test_alignment() + { + var layout = layout_alignment_Component.createObject(container); + layout.width = 100; + layout.height = 40; + + compare(itemRect(layout.children[0]), [ 0, 0, 20, 40]); + compare(itemRect(layout.children[1]), [20, 10, 20, 20]); + compare(itemRect(layout.children[2]), [40, 0, 20, 20]); + compare(itemRect(layout.children[3]), [60, 10, 20, 20]); + compare(itemRect(layout.children[4]), [80, 20, 20, 20]); + layout.destroy(); + } + + Component { + id: layout_sizeHintNormalization_Component + GridLayout { + columnSpacing: 0 + rowSpacing: 0 + Rectangle { + id: r1 + color: "red" + Layout.minimumWidth: 1 + Layout.preferredWidth: 2 + Layout.maximumWidth: 3 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + Layout.fillWidth: true + } + } + } + + function test_sizeHintNormalization_data() { + return [ + { tag: "fallbackValues", widthHints: [-1, -1, -1], implicitWidth: 42, expected:[0,42,Number.POSITIVE_INFINITY]}, + { tag: "acceptZeroWidths", widthHints: [0, 0, 0], implicitWidth: 42, expected:[0,0,0]}, + { tag: "123", widthHints: [1,2,3], expected:[1,2,3]}, + { tag: "132", widthHints: [1,3,2], expected:[1,2,2]}, + { tag: "213", widthHints: [2,1,3], expected:[2,2,3]}, + { tag: "231", widthHints: [2,3,1], expected:[1,1,1]}, + { tag: "321", widthHints: [3,2,1], expected:[1,1,1]}, + { tag: "312", widthHints: [3,1,2], expected:[2,2,2]}, + + { tag: "1i3", widthHints: [1,-1,3], implicitWidth: 2, expected:[1,2,3]}, + { tag: "1i2", widthHints: [1,-1,2], implicitWidth: 3, expected:[1,2,2]}, + { tag: "2i3", widthHints: [2,-1,3], implicitWidth: 1, expected:[2,2,3]}, + { tag: "2i1", widthHints: [2,-1,1], implicitWidth: 3, expected:[1,1,1]}, + { tag: "3i1", widthHints: [3,-1,1], implicitWidth: 2, expected:[1,1,1]}, + { tag: "3i2", widthHints: [3,-1,2], implicitWidth: 1, expected:[2,2,2]}, + ]; + } + + function test_sizeHintNormalization(data) { + var layout = layout_sizeHintNormalization_Component.createObject(container); + if (data.implicitWidth !== undefined) { + layout.children[0].implicitWidth = data.implicitWidth + } + layout.children[0].Layout.minimumWidth = data.widthHints[0]; + layout.children[0].Layout.preferredWidth = data.widthHints[1]; + layout.children[0].Layout.maximumWidth = data.widthHints[2]; + wait(0); // Trigger processEvents() (allow LayoutRequest to be processed) + var normalizedResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth] + compare(normalizedResult, data.expected); + layout.destroy(); + } + + Component { + id: layout_sizeHint_Component + RowLayout { + property int implicitWidthChangedCount : 0 + onImplicitWidthChanged: { ++implicitWidthChangedCount } + GridLayout { + columnSpacing: 0 + rowSpacing: 0 + Rectangle { + id: r1 + color: "red" + Layout.minimumWidth: 1 + Layout.preferredWidth: 2 + Layout.maximumWidth: 3 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + Layout.fillWidth: true + } + } + } + } + + function test_sizeHint_data() { + return [ + { tag: "propagateNone", layoutHints: [10, 20, 30], childHints: [11, 21, 31], expected:[10, 20, 30]}, + { tag: "propagateMinimumWidth", layoutHints: [-1, 20, 30], childHints: [10, 21, 31], expected:[10, 20, 30]}, + { tag: "propagatePreferredWidth", layoutHints: [10, -1, 30], childHints: [11, 20, 31], expected:[10, 20, 30]}, + { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, 30]}, + { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, 30]}, + { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, 30]}, + { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, 31]}, + { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, 99]}, + { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, 99]}, + { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, 31]}, + { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, 19]}, + { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, 9]}, + ]; + } + + function itemSizeHints(item) { + return [item.Layout.minimumWidth, item.implicitWidth, item.Layout.maximumWidth] + } + + function test_sizeHint(data) { + var layout = layout_sizeHint_Component.createObject(container) + + var grid = layout.children[0] + grid.Layout.minimumWidth = data.layoutHints[0] + grid.Layout.preferredWidth = data.layoutHints[1] + grid.Layout.maximumWidth = data.layoutHints[2] + + var child = grid.children[0] + if (data.implicitWidth !== undefined) { + child.implicitWidth = data.implicitWidth + } + child.Layout.minimumWidth = data.childHints[0] + child.Layout.preferredWidth = data.childHints[1] + child.Layout.maximumWidth = data.childHints[2] + + var effectiveSizeHintResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth] + compare(effectiveSizeHintResult, data.expected) + layout.destroy() + } + + function test_sizeHintPropagationCount() { + var layout = layout_sizeHint_Component.createObject(container) + var child = layout.children[0].children[0] + + child.Layout.minimumWidth = -1 + compare(itemSizeHints(layout), [0, 2, 3]) + child.Layout.preferredWidth = -1 + compare(itemSizeHints(layout), [0, 0, 3]) + child.Layout.maximumWidth = -1 + compare(itemSizeHints(layout), [0, 0, Number.POSITIVE_INFINITY]) + layout.Layout.maximumWidth = 1000 + compare(itemSizeHints(layout), [0, 0, 1000]) + layout.Layout.maximumWidth = -1 + compare(itemSizeHints(layout), [0, 0, Number.POSITIVE_INFINITY]) + + layout.implicitWidthChangedCount = 0 + child.Layout.minimumWidth = 10 + compare(itemSizeHints(layout), [10, 10, Number.POSITIVE_INFINITY]) + compare(layout.implicitWidthChangedCount, 1) + + child.Layout.preferredWidth = 20 + compare(itemSizeHints(layout), [10, 20, Number.POSITIVE_INFINITY]) + compare(layout.implicitWidthChangedCount, 2) + + child.Layout.maximumWidth = 30 + compare(itemSizeHints(layout), [10, 20, 30]) + compare(layout.implicitWidthChangedCount, 2) + + child.Layout.maximumWidth = 15 + compare(itemSizeHints(layout), [10, 15, 15]) + compare(layout.implicitWidthChangedCount, 3) + + child.Layout.maximumWidth = 30 + compare(itemSizeHints(layout), [10, 20, 30]) + compare(layout.implicitWidthChangedCount, 4) + + layout.Layout.maximumWidth = 29 + compare(layout.Layout.maximumWidth, 29) + layout.Layout.maximumWidth = -1 + compare(layout.Layout.maximumWidth, 30) + + layout.destroy() + } + + Component { + id: layout_change_implicitWidth_during_rearrange + ColumnLayout { + width: 100 + height: 20 + RowLayout { + spacing: 0 + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: false + implicitWidth: height + color: "red" + } + Rectangle { + Layout.fillHeight: true + Layout.fillWidth: true + color: "blue" + } + } + } + } + + function test_change_implicitWidth_during_rearrange() { + var layout = layout_change_implicitWidth_during_rearrange.createObject(container) + var red = layout.children[0].children[0] + var blue = layout.children[0].children[1] + waitForRendering(layout); + tryCompare(red, 'width', 20) + tryCompare(blue, 'width', 80) + layout.height = 40 + tryCompare(red, 'width', 40) + tryCompare(blue, 'width', 60) + layout.destroy() + } + + Component { + id: layout_addIgnoredItem_Component + RowLayout { + spacing: 0 + Rectangle { + id: r + } + } + } + + function test_addIgnoredItem() + { + var layout = layout_addIgnoredItem_Component.createObject(container) + compare(layout.implicitWidth, 0) + compare(layout.implicitHeight, 0) + var r = layout.children[0] + r.Layout.preferredWidth = 20 + r.Layout.preferredHeight = 30 + compare(layout.implicitWidth, 20) + compare(layout.implicitHeight, 30) + + layout.destroy(); + } + + + Component { + id: layout_rowLayout_Component + RowLayout { + } + } + + function test_stretchItem_data() + { + return [ + { expectedWidth: 0}, + { preferredWidth: 20, expectedWidth: 20}, + { preferredWidth: 0, expectedWidth: 0}, + { preferredWidth: 20, fillWidth: true, expectedWidth: 100}, + { width: 20, fillWidth: true, expectedWidth: 100}, + { width: 0, fillWidth: true, expectedWidth: 100}, + { preferredWidth: 0, fillWidth: true, expectedWidth: 100}, + { preferredWidth: 1, maximumWidth: 0, fillWidth: true, expectedWidth: 0}, + { preferredWidth: 0, minimumWidth: 1, expectedWidth: 1}, + ]; + } + + function test_stretchItem(data) + { + var layout = layout_rowLayout_Component.createObject(container) + var r = layoutItem_Component.createObject(layout) + // Reset previously relevant properties + r.width = 0 + r.implicitWidth = 0 + compare(layout.implicitWidth, 0) + + if (data.preferredWidth !== undefined) + r.Layout.preferredWidth = data.preferredWidth + if (data.fillWidth !== undefined) + r.Layout.fillWidth = data.fillWidth + if (data.width !== undefined) + r.width = data.width + if (data.minimumWidth !== undefined) + r.Layout.minimumWidth = data.minimumWidth + if (data.maximumWidth !== undefined) + r.Layout.maximumWidth = data.maximumWidth + + layout.width = 100 + + compare(r.width, data.expectedWidth) + + layout.destroy(); + } + + Component { + id: layout_alignToPixelGrid_Component + RowLayout { + spacing: 2 + Rectangle { + implicitWidth: 10 + implicitHeight: 10 + Layout.alignment: Qt.AlignVCenter + } + Rectangle { + implicitWidth: 10 + implicitHeight: 10 + Layout.alignment: Qt.AlignVCenter + } + } + } + function test_alignToPixelGrid() + { + var layout = layout_alignToPixelGrid_Component.createObject(container) + layout.width = 21 + layout.height = 21 + var r0 = layout.children[0] + compare(r0.x, 0) // 0.0 + compare(r0.y, 6) // 5.5 + var r1 = layout.children[1] + compare(r1.x, 12) // 11.5 + compare(r1.y, 6) // 5.5 + layout.destroy(); + } + + Component { + id: test_distributeToPixelGrid_Component + RowLayout { + spacing: 0 + Rectangle { + color: 'red' + Layout.minimumWidth: 10 + Layout.preferredWidth: 50 + Layout.maximumWidth: 90 + Layout.fillWidth: true + implicitHeight: 10 + } + Rectangle { + color: 'red' + Layout.minimumWidth: 10 + Layout.preferredWidth: 20 + Layout.maximumWidth: 90 + Layout.fillWidth: true + implicitHeight: 10 + } + Rectangle { + color: 'red' + Layout.minimumWidth: 10 + Layout.preferredWidth: 70 + Layout.maximumWidth: 90 + Layout.fillWidth: true + implicitHeight: 10 + } + } + } + + function test_distributeToPixelGrid_data() { + return [ + { tag: "narrow", spacing: 0, width: 60 }, + { tag: "belowPreferred", spacing: 0, width: 130 }, + { tag: "belowPreferredWithSpacing", spacing: 10, width: 130 }, + { tag: "abovePreferred", spacing: 0, width: 150 }, + { tag: "stretchSomethingToMaximum", spacing: 0, width: 240, + expected: [90, 60, 90] }, + { tag: "minSizeHasFractions", spacing: 2, width: 31 + 4, hints: [{min: 10+1/3}, {min: 10+1/3}, {min: 10+1/3}], + /*expected: [11, 11, 11]*/ }, /* verify that nothing gets allocated a size smaller than its minimum */ + { tag: "maxSizeHasFractions", spacing: 2, width: 271 + 4, hints: [{max: 90+1/3}, {max: 90+1/3}, {max: 90+1/3}], + /*expected: [90, 90, 90]*/ }, /* verify that nothing gets allocated a size larger than its maximum */ + { tag: "fixedSizeHasFractions", spacing: 2, width: 31 + 4, hints: [{min: 10+1/3, max: 10+1/3}, {min: 10+1/3, max: 10+1/3}, {min: 10+1/3, max: 10+1/3}], + /*expected: [11, 11, 11]*/ }, /* verify that nothing gets allocated a size smaller than its minimum */ + ]; + } + + function test_distributeToPixelGrid(data) + { + // CONFIGURATION + var layout = test_distributeToPixelGrid_Component.createObject(container) + layout.spacing = data.spacing + layout.width = data.width + layout.height = 10 + var kids = layout.children + + if (data.hasOwnProperty('hints')) { + var hints = data.hints + for (var i = 0; i < hints.length; ++i) { + var h = hints[i] + if (h.hasOwnProperty('min')) + kids[i].Layout.minimumWidth = h.min + if (h.hasOwnProperty('pref')) + kids[i].Layout.preferredWidth = h.pref + if (h.hasOwnProperty('max')) + kids[i].Layout.maximumWidth = h.max + } + } + waitForRendering(layout) + + var sum = 2 * layout.spacing + // TEST + for (var i = 0; i < kids.length; ++i) { + compare(kids[i].x % 1, 0) // checks if position is a whole integer + // verify if the items are within the size constraints as specified + verify(kids[i].width >= kids[i].Layout.minimumWidth) + verify(kids[i].width <= kids[i].Layout.maximumWidth) + if (data.hasOwnProperty('expected')) + compare(kids[i].width, data.expected[i]) + sum += kids[i].width + } + fuzzyCompare(sum, layout.width, 1) + + layout.destroy(); + } + + + + Component { + id: layout_deleteLayout + ColumnLayout { + property int dummyproperty: 0 // yes really - its needed + RowLayout { + Text { text: "label1" } // yes, both are needed + Text { text: "label2" } + } + } + } + + function test_destroyLayout() + { + var layout = layout_deleteLayout.createObject(container) + layout.children[0].children[0].visible = true + layout.visible = false + layout.destroy() // Do not crash + } + + function test_sizeHintWithHiddenChildren(data) { + var layout = layout_sizeHint_Component.createObject(container) + var grid = layout.children[0] + var child = grid.children[0] + + // Implicit sizes are not affected by the visibility of the parent layout. + // This is in order for the layout to know the preferred size it should show itself at. + compare(grid.visible, true) // LAYOUT SHOWN + compare(grid.implicitWidth, 2); + child.visible = false + compare(grid.implicitWidth, 0); + child.visible = true + compare(grid.implicitWidth, 2); + + grid.visible = false // LAYOUT HIDDEN + compare(grid.implicitWidth, 2); + child.visible = false + expectFail('', 'If GridLayout is hidden, GridLayout is not notified when child is explicitly hidden') + compare(grid.implicitWidth, 0); + child.visible = true + compare(grid.implicitWidth, 2); + + layout.destroy(); + } + + Component { + id: row_sizeHint_Component + Row { + Rectangle { + id: r1 + color: "red" + width: 2 + height: 20 + } + } + } + + function test_sizeHintWithHiddenChildrenForRow(data) { + var row = row_sizeHint_Component.createObject(container) + var child = row.children[0] + compare(row.visible, true) // POSITIONER SHOWN + compare(row.implicitWidth, 2); + child.visible = false + tryCompare(row, 'implicitWidth', 0); + child.visible = true + tryCompare(row, 'implicitWidth', 2); + + row.visible = false // POSITIONER HIDDEN + compare(row.implicitWidth, 2); + child.visible = false + expectFail('', 'If Row is hidden, Row is not notified when child is explicitly hidden') + compare(row.implicitWidth, 0); + child.visible = true + compare(row.implicitWidth, 2); + } + + Component { + id: rearrangeNestedLayouts_Component + RowLayout { + id: layout + anchors.fill: parent + width: 200 + height: 20 + RowLayout { + id: row + spacing: 0 + + Rectangle { + id: fixed + color: 'red' + implicitWidth: 20 + implicitHeight: 20 + } + Rectangle { + id: filler + color: 'grey' + Layout.fillWidth: true + implicitHeight: 20 + } + } + } + } + + function test_rearrangeNestedLayouts() + { + var layout = rearrangeNestedLayouts_Component.createObject(container) + var fixed = layout.children[0].children[0] + var filler = layout.children[0].children[1] + + compare(itemRect(fixed), [0,0,20,20]) + compare(itemRect(filler), [20,0,180,20]) + + fixed.implicitWidth = 100 + waitForRendering(layout) + compare(itemRect(fixed), [0,0,100,20]) + compare(itemRect(filler), [100,0,100,20]) + } + + Component { + id: changeChildrenOfHiddenLayout_Component + RowLayout { + property int childCount: 1 + Repeater { + model: parent.childCount + Text { + text: 'Just foo it' + } + } + } + } + function test_changeChildrenOfHiddenLayout() + { + var layout = changeChildrenOfHiddenLayout_Component.createObject(container) + var child = layout.children[0] + waitForRendering(layout) + layout.visible = false + waitForRendering(layout) + // Remove and add children to the hidden layout.. + layout.childCount = 0 + waitForRendering(layout) + layout.childCount = 1 + waitForRendering(layout) + layout.destroy() + } + } +} diff --git a/tests/auto/quick/qquicklayouts/qquicklayouts.pro b/tests/auto/quick/qquicklayouts/qquicklayouts.pro new file mode 100644 index 0000000000..9ed3e076be --- /dev/null +++ b/tests/auto/quick/qquicklayouts/qquicklayouts.pro @@ -0,0 +1,13 @@ +QT += core-private gui-private qml-private +TEMPLATE=app +TARGET=tst_qquicklayouts + +CONFIG += qmltestcase +SOURCES += tst_qquicklayouts.cpp + +TESTDATA = data/* + +OTHER_FILES += \ + data/tst_rowlayout.qml \ + data/tst_gridlayout.qml + diff --git a/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp new file mode 100644 index 0000000000..373019091f --- /dev/null +++ b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include <QtQuickTest/quicktest.h> +QUICK_TEST_MAIN(qquicklayouts) diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 405acad165..658ffa1f57 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -8264,99 +8264,9 @@ void tst_QQuickListView::keyNavigationEnabled() QCOMPARE(listView->currentIndex(), 1); } -static bool testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx) -{ - QHash<QQuickItem*, int> uniqueItems; - - int skip = 0; - for (int i = 0; i < priv->visibleItems.count(); ++i) { - FxViewItem *item = priv->visibleItems.at(i); - if (!item) { - *failItem = Q_NULLPTR; - return false; - } -#if 0 - qDebug() << "\t" << item->index - << item->item - << item->position() - << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible"); -#endif - if (item->index == -1) { - ++skip; - } else if (item->index != priv->visibleIndex + i - skip) { - *nonUnique = false; - *failItem = item; - *expectedIdx = priv->visibleIndex + i - skip; - return false; - } else if (uniqueItems.contains(item->item)) { - *nonUnique = true; - *failItem = item; - *expectedIdx = uniqueItems.find(item->item).value(); - return false; - } - - uniqueItems.insert(item->item, item->index); - } - - return true; -} - -class QTBUG_48870_Model : public QAbstractListModel -{ - Q_OBJECT - -public: - - QTBUG_48870_Model() - : QAbstractListModel() - , m_rowCount(20) - { - QTimer *t = new QTimer(this); - t->setInterval(500); - t->start(); - - qsrand(qHash(QDateTime::currentDateTime())); - connect(t, &QTimer::timeout, this, &QTBUG_48870_Model::updateModel); - } - - int rowCount(const QModelIndex &) const - { - return m_rowCount; - } - - QVariant data(const QModelIndex &, int) const - { - return QVariant(); - } - -public Q_SLOTS: - void updateModel() - { - if (m_rowCount > 10) { - for (int i = 0; i < 10; ++i) { - int rnum = qrand() % m_rowCount; - beginRemoveRows(QModelIndex(), rnum, rnum); - m_rowCount--; - endRemoveRows(); - } - } - if (m_rowCount < 20) { - for (int i = 0; i < 10; ++i) { - int rnum = qrand() % m_rowCount; - beginInsertRows(QModelIndex(), rnum, rnum); - m_rowCount++; - endInsertRows(); - } - } - } - -private: - int m_rowCount; -}; - void tst_QQuickListView::QTBUG_48870_fastModelUpdates() { - QTBUG_48870_Model model; + StressTestModel model; QScopedPointer<QQuickView> window(createView()); QQmlContext *ctxt = window->rootContext(); diff --git a/tests/auto/quick/qquicktextedit/BLACKLIST b/tests/auto/quick/qquicktextedit/BLACKLIST index 297146195b..492d81531a 100644 --- a/tests/auto/quick/qquicktextedit/BLACKLIST +++ b/tests/auto/quick/qquicktextedit/BLACKLIST @@ -1,6 +1,2 @@ [mouseSelection] * -[undo] -* -[undo_keypressevents] -* diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 50e06e5663..47baaaece8 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -108,6 +108,7 @@ private slots: void selectionOnFocusOut(); void focusOnPress(); void selection(); + void overwriteMode(); void isRightToLeft_data(); void isRightToLeft(); void keySelection(); @@ -1466,6 +1467,74 @@ void tst_qquicktextedit::selection() QVERIFY(textEditObject->selectedText().isNull()); } +void tst_qquicktextedit::overwriteMode() +{ + QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; }"; + QQmlComponent textEditComponent(&engine); + textEditComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create()); + QVERIFY(textEdit != 0); + + QSignalSpy spy(textEdit, SIGNAL(overwriteModeChanged(bool))); + + QQuickWindow window; + textEdit->setParentItem(window.contentItem()); + window.show(); + window.requestActivate(); + QTest::qWaitForWindowActive(&window); + + QVERIFY(textEdit->hasActiveFocus()); + + textEdit->setOverwriteMode(true); + QCOMPARE(spy.count(), 1); + QCOMPARE(true, textEdit->overwriteMode()); + textEdit->setOverwriteMode(false); + QCOMPARE(spy.count(), 2); + QCOMPARE(false, textEdit->overwriteMode()); + + QVERIFY(!textEdit->overwriteMode()); + QString insertString = "Some first text"; + for (int j = 0; j < insertString.length(); j++) + QTest::keyClick(&window, insertString.at(j).toLatin1()); + + QCOMPARE(textEdit->text(), QString("Some first text")); + + textEdit->setOverwriteMode(true); + QCOMPARE(spy.count(), 3); + textEdit->setCursorPosition(5); + + insertString = "shiny"; + for (int j = 0; j < insertString.length(); j++) + QTest::keyClick(&window, insertString.at(j).toLatin1()); + QCOMPARE(textEdit->text(), QString("Some shiny text")); + + textEdit->setCursorPosition(textEdit->text().length()); + QTest::keyClick(&window, Qt::Key_Enter); + + textEdit->setOverwriteMode(false); + QCOMPARE(spy.count(), 4); + + insertString = "Second paragraph"; + + for (int j = 0; j < insertString.length(); j++) + QTest::keyClick(&window, insertString.at(j).toLatin1()); + QCOMPARE(textEdit->lineCount(), 2); + + textEdit->setCursorPosition(15); + + QCOMPARE(textEdit->cursorPosition(), 15); + + textEdit->setOverwriteMode(true); + QCOMPARE(spy.count(), 5); + + insertString = " blah"; + for (int j = 0; j < insertString.length(); j++) + QTest::keyClick(&window, insertString.at(j).toLatin1()); + QCOMPARE(textEdit->lineCount(), 2); + + QCOMPARE(textEdit->text(), QString("Some shiny text blah\nSecond paragraph")); +} + void tst_qquicktextedit::isRightToLeft_data() { QTest::addColumn<QString>("text"); diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index abb2c5b773..c899290594 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -105,6 +105,7 @@ private slots: void wrap(); void selection(); void persistentSelection(); + void overwriteMode(); void isRightToLeft_data(); void isRightToLeft(); void moveCursorSelection_data(); @@ -777,6 +778,48 @@ void tst_qquicktextinput::persistentSelection() QCOMPARE(input->property("selected").toString(), QLatin1String("ell")); } +void tst_qquicktextinput::overwriteMode() +{ + QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; }"; + QQmlComponent textInputComponent(&engine); + textInputComponent.setData(componentStr.toLatin1(), QUrl()); + QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create()); + QVERIFY(textInput != 0); + + QSignalSpy spy(textInput, SIGNAL(overwriteModeChanged(bool))); + + QQuickWindow window; + textInput->setParentItem(window.contentItem()); + window.show(); + window.requestActivate(); + QTest::qWaitForWindowActive(&window); + + QVERIFY(textInput->hasActiveFocus()); + + textInput->setOverwriteMode(true); + QCOMPARE(spy.count(), 1); + QCOMPARE(true, textInput->overwriteMode()); + textInput->setOverwriteMode(false); + QCOMPARE(spy.count(), 2); + QCOMPARE(false, textInput->overwriteMode()); + + QVERIFY(!textInput->overwriteMode()); + QString insertString = "Some first text"; + for (int j = 0; j < insertString.length(); j++) + QTest::keyClick(&window, insertString.at(j).toLatin1()); + + QCOMPARE(textInput->text(), QString("Some first text")); + + textInput->setOverwriteMode(true); + QCOMPARE(spy.count(), 3); + textInput->setCursorPosition(5); + + insertString = "shiny"; + for (int j = 0; j < insertString.length(); j++) + QTest::keyClick(&window, insertString.at(j).toLatin1()); + QCOMPARE(textInput->text(), QString("Some shiny text")); +} + void tst_qquicktextinput::isRightToLeft_data() { QTest::addColumn<QString>("text"); diff --git a/tests/auto/quick/qquickwindow/data/windowattached.qml b/tests/auto/quick/qquickwindow/data/windowattached.qml index a9f052d55e..9d61a02452 100644 --- a/tests/auto/quick/qquickwindow/data/windowattached.qml +++ b/tests/auto/quick/qquickwindow/data/windowattached.qml @@ -9,6 +9,7 @@ Rectangle { property Item contentItem: root.Window.contentItem property int windowWidth: root.Window.width property int windowHeight: root.Window.height + property var window: root.Window.window Text { objectName: "rectangleWindowText" anchors.centerIn: parent @@ -26,6 +27,7 @@ Rectangle { property Item contentItem: Window.contentItem property int windowWidth: Window.width property int windowHeight: Window.height + property var window: Window.window } } } diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 5d1897ab2d..ff8c80e3ae 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -2033,6 +2033,7 @@ void tst_qquickwindow::attachedProperty() QCOMPARE(view.rootObject()->property("contentItem").value<QQuickItem*>(), view.contentItem()); QCOMPARE(view.rootObject()->property("windowWidth").toInt(), view.width()); QCOMPARE(view.rootObject()->property("windowHeight").toInt(), view.height()); + QCOMPARE(view.rootObject()->property("window").value<QQuickView*>(), &view); QQuickWindow *innerWindow = view.rootObject()->findChild<QQuickWindow*>("extraWindow"); QVERIFY(innerWindow); @@ -2045,6 +2046,13 @@ void tst_qquickwindow::attachedProperty() QCOMPARE(text->property("contentItem").value<QQuickItem*>(), innerWindow->contentItem()); QCOMPARE(text->property("windowWidth").toInt(), innerWindow->width()); QCOMPARE(text->property("windowHeight").toInt(), innerWindow->height()); + QCOMPARE(text->property("window").value<QQuickWindow*>(), innerWindow); + + text->setParentItem(0); + QVERIFY(!text->property("contentItem").value<QQuickItem*>()); + QCOMPARE(text->property("windowWidth").toInt(), 0); + QCOMPARE(text->property("windowHeight").toInt(), 0); + QVERIFY(!text->property("window").value<QQuickWindow*>()); } class RenderJob : public QRunnable diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index f25a28d45b..13bd6d78e2 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -21,6 +21,7 @@ PRIVATETESTS += \ qquickfontloader_static \ qquickfontmetrics \ qquickimageprovider \ + qquicklayouts \ qquickpath \ qquicksmoothedanimation \ qquickspringanimation \ diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 09c88acbe6..ab58aee648 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -35,6 +35,7 @@ #include <QtTest/QTest> #include <private/qquickwindow_p.h> +#include <private/qquickitemview_p_p.h> QQuickView *QQuickViewTestUtil::createView() @@ -347,6 +348,85 @@ QList<QPair<QString,QString> > QQuickViewTestUtil::ListRange::getModelDataValues return data; } +QQuickViewTestUtil::StressTestModel::StressTestModel() + : QAbstractListModel() + , m_rowCount(20) +{ + QTimer *t = new QTimer(this); + t->setInterval(500); + t->start(); + + qsrand(qHash(QDateTime::currentDateTime())); + connect(t, &QTimer::timeout, this, &StressTestModel::updateModel); +} + +int QQuickViewTestUtil::StressTestModel::rowCount(const QModelIndex &) const +{ + return m_rowCount; +} + +QVariant QQuickViewTestUtil::StressTestModel::data(const QModelIndex &, int) const +{ + return QVariant(); +} + +void QQuickViewTestUtil::StressTestModel::updateModel() +{ + if (m_rowCount > 10) { + for (int i = 0; i < 10; ++i) { + int rnum = qrand() % m_rowCount; + beginRemoveRows(QModelIndex(), rnum, rnum); + m_rowCount--; + endRemoveRows(); + } + } + if (m_rowCount < 20) { + for (int i = 0; i < 10; ++i) { + int rnum = qrand() % m_rowCount; + beginInsertRows(QModelIndex(), rnum, rnum); + m_rowCount++; + endInsertRows(); + } + } +} + +bool QQuickViewTestUtil::testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx) +{ + QHash<QQuickItem*, int> uniqueItems; + + int skip = 0; + for (int i = 0; i < priv->visibleItems.count(); ++i) { + FxViewItem *item = priv->visibleItems.at(i); + if (!item) { + *failItem = Q_NULLPTR; + return false; + } +#if 0 + qDebug() << "\t" << item->index + << item->item + << item->position() + << (!item->item || QQuickItemPrivate::get(item->item)->culled ? "hidden" : "visible"); +#endif + if (item->index == -1) { + ++skip; + } else if (item->index != priv->visibleIndex + i - skip) { + *nonUnique = false; + *failItem = item; + *expectedIdx = priv->visibleIndex + i - skip; + return false; + } else if (uniqueItems.contains(item->item)) { + *nonUnique = true; + *failItem = item; + *expectedIdx = uniqueItems.find(item->item).value(); + return false; + } + + uniqueItems.insert(item->item, item->index); + } + + return true; +} + namespace QQuickTouchUtils { /* QQuickWindow does event compression and only delivers events just diff --git a/tests/auto/quick/shared/viewtestutil.h b/tests/auto/quick/shared/viewtestutil.h index 0f50180809..b11d5e4859 100644 --- a/tests/auto/quick/shared/viewtestutil.h +++ b/tests/auto/quick/shared/viewtestutil.h @@ -34,6 +34,8 @@ #include <QtCore/QAbstractListModel> QT_FORWARD_DECLARE_CLASS(QQuickView) +QT_FORWARD_DECLARE_CLASS(QQuickItemViewPrivate) +QT_FORWARD_DECLARE_CLASS(FxViewItem) namespace QQuickViewTestUtil { @@ -154,6 +156,26 @@ namespace QQuickViewTestUtil for (; f != replaced.end(); ++f, ++t) *t = *f; } + + class StressTestModel : public QAbstractListModel + { + Q_OBJECT + + public: + + StressTestModel(); + + int rowCount(const QModelIndex &) const; + QVariant data(const QModelIndex &, int) const; + + public Q_SLOTS: + void updateModel(); + + private: + int m_rowCount; + }; + + bool testVisibleItems(const QQuickItemViewPrivate *priv, bool *nonUnique, FxViewItem **failItem, int *expectedIdx); } namespace QQuickTouchUtils { diff --git a/tests/manual/scenegraph_lancelot/data/text/textedit_multiline_selected_linebreaks_and_linewraps.qml b/tests/manual/scenegraph_lancelot/data/text/textedit_multiline_selected_linebreaks_and_linewraps.qml new file mode 100644 index 0000000000..a1004d0374 --- /dev/null +++ b/tests/manual/scenegraph_lancelot/data/text/textedit_multiline_selected_linebreaks_and_linewraps.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 + +Item { + width: 200 + height: 480 + + TextEdit { + id: textEdit + anchors.centerIn: parent + font.family: "Arial" + font.pixelSize: 64 + width: 200 + textFormat: TextEdit.RichText + wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere + text: "ABC ABC<br>ABC" + + Component.onCompleted: { + textEdit.selectAll() + } + } + +} diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index af6b272fc6..dcb9709bb8 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -85,7 +85,7 @@ QVariantList findImportsInAst(QQmlJS::AST::UiHeaderItemList *headerItemList, con if (!importNode->fileName.isEmpty()) { QString name = importNode->fileName.toString(); import[QStringLiteral("name")] = name; - if (name.endsWith(QStringLiteral(".js"))) { + if (name.endsWith(QLatin1String(".js"))) { import[QStringLiteral("type")] = QStringLiteral("javascript"); } else { import[QStringLiteral("type")] = QStringLiteral("directory"); @@ -188,7 +188,7 @@ QVariantList findPathsForModuleImports(const QVariantList &imports) for (int i = 0; i < importsCopy.length(); ++i) { QVariantMap import = qvariant_cast<QVariantMap>(importsCopy[i]); - if (import[QStringLiteral("type")] == QStringLiteral("module")) { + if (import[QStringLiteral("type")] == QLatin1String("module")) { QString path = resolveImportPath(import.value(QStringLiteral("name")).toString(), import.value(QStringLiteral("version")).toString()); if (!path.isEmpty()) import[QStringLiteral("path")] = path; @@ -319,9 +319,9 @@ QVariantList findQmlImportsInFile(const QString &filePath) QFile f; if (f.open(stdin, QIODevice::ReadOnly)) imports = findQmlImportsInQmlCode(QLatin1String("<stdin>"), QString::fromUtf8(f.readAll())); - } else if (filePath.endsWith(QStringLiteral(".qml"))) { + } else if (filePath.endsWith(QLatin1String(".qml"))) { imports = findQmlImportsInQmlFile(filePath); - } else if (filePath.endsWith(QStringLiteral(".js"))) { + } else if (filePath.endsWith(QLatin1String(".js"))) { imports = findQmlImportsInJavascriptFile(filePath); } @@ -382,10 +382,10 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir) continue; // skip obvious build output directories - if (path.contains(QStringLiteral("Debug-iphoneos")) || path.contains(QStringLiteral("Release-iphoneos")) || - path.contains(QStringLiteral("Debug-iphonesimulator")) || path.contains(QStringLiteral("Release-iphonesimulator")) + if (path.contains(QLatin1String("Debug-iphoneos")) || path.contains(QLatin1String("Release-iphoneos")) || + path.contains(QLatin1String("Debug-iphonesimulator")) || path.contains(QLatin1String("Release-iphonesimulator")) #ifdef Q_OS_WIN - || path.contains(QStringLiteral("/release/")) || path.contains(QStringLiteral("/debug/")) + || path.contains(QLatin1String("/release/")) || path.contains(QLatin1String("/debug/")) #endif ){ continue; @@ -404,7 +404,7 @@ QSet<QString> importModulePaths(QVariantList imports) { QVariantMap import = qvariant_cast<QVariantMap>(importVariant); QString path = import.value(QStringLiteral("path")).toString(); QString type = import.value(QStringLiteral("type")).toString(); - if (type == QStringLiteral("module") && !path.isEmpty()) + if (type == QLatin1String("module") && !path.isEmpty()) ret.insert(QDir(path).canonicalPath()); } return ret; diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 776e19fdf4..7870e3a9df 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -51,9 +51,11 @@ #include <QtCore/private/qobject_p.h> #include <QtCore/private/qmetaobject_p.h> +#include <QRegularExpression> #include <iostream> #include <algorithm> +#include "qmltypereader.h" #include "qmlstreamwriter.h" #ifdef QT_SIMULATOR @@ -728,7 +730,7 @@ void sigSegvHandler(int) { void printUsage(const QString &appName) { std::cerr << qPrintable(QString( - "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies <dependencies.json>] module.uri version [module/import/path]\n" + "Usage: %1 [-v] [-noinstantiate] [-defaultplatform] [-[non]relocatable] [-dependencies <dependencies.json>] [-merge <file-to-merge.qmltypes>] module.uri version [module/import/path]\n" " %1 [-v] [-noinstantiate] -path path/to/qmldir/directory [version]\n" " %1 [-v] -builtins\n" "Example: %1 Qt.labs.folderlistmodel 2.0 /home/user/dev/qt-install/imports").arg( @@ -982,6 +984,7 @@ int main(int argc, char *argv[]) QString pluginImportVersion; bool relocatable = true; QString dependenciesFile; + QString mergeFile; enum Action { Uri, Path, Builtins }; Action action = Uri; { @@ -1000,6 +1003,13 @@ int main(int argc, char *argv[]) return EXIT_INVALIDARGUMENTS; } dependenciesFile = args.at(iArg); + } else if (arg == QLatin1String("--merge") + || arg == QLatin1String("-merge")) { + if (++iArg == args.size()) { + std::cerr << "missing merge file" << std::endl; + return EXIT_INVALIDARGUMENTS; + } + mergeFile = args.at(iArg); } else if (arg == QLatin1String("--notrelocatable") || arg == QLatin1String("-notrelocatable") || arg == QLatin1String("--nonrelocatable") @@ -1061,6 +1071,26 @@ int main(int argc, char *argv[]) QDir::setCurrent(pluginImportPath); engine.addImportPath(pluginImportPath); } + + // Merge file. + QStringList mergeDependencies; + QString mergeComponents; + if (!mergeFile.isEmpty()) { + QStringList merge = readQmlTypes(mergeFile); + if (!merge.isEmpty()) { + QRegularExpression re("(\\w+\\.*\\w*\\s*\\d+\\.\\d+)"); + QRegularExpressionMatchIterator i = re.globalMatch(merge[1]); + while (i.hasNext()) { + QRegularExpressionMatch m = i.next(); + QString d = m.captured(1); + mergeDependencies << m.captured(1); + } + mergeComponents = merge [2]; + } + } + + // Dependencies. + bool calculateDependencies = !pluginImportUri.isEmpty() && !pluginImportVersion.isEmpty(); QStringList dependencies; if (!dependenciesFile.isEmpty()) @@ -1210,6 +1240,13 @@ int main(int argc, char *argv[]) "// '%1 %2'\n" "\n").arg(QFileInfo(args.at(0)).baseName(), args.mid(1).join(QLatin1Char(' ')))); qml.writeStartObject("Module"); + + // Insert merge dependencies. + if (!mergeDependencies.isEmpty()) { + dependencies << mergeDependencies; + } + compactDependencies(&dependencies); + QStringList quotedDependencies; foreach (const QString &dep, dependencies) quotedDependencies << enquote(dep); @@ -1236,6 +1273,9 @@ int main(int argc, char *argv[]) if (pluginImportUri.isEmpty()) dumper.writeEasingCurve(); + // Insert merge file. + qml.write(mergeComponents); + qml.writeEndObject(); qml.writeEndDocument(); diff --git a/tools/qmlplugindump/qmlplugindump.pro b/tools/qmlplugindump/qmlplugindump.pro index 6fdcd349d1..e45a7fad83 100644 --- a/tools/qmlplugindump/qmlplugindump.pro +++ b/tools/qmlplugindump/qmlplugindump.pro @@ -6,10 +6,12 @@ QTPLUGIN.platforms = qminimal SOURCES += \ main.cpp \ - qmlstreamwriter.cpp + qmlstreamwriter.cpp \ + qmltypereader.cpp HEADERS += \ - qmlstreamwriter.h + qmlstreamwriter.h \ + qmltypereader.h macx { # Prevent qmlplugindump from popping up in the dock when launched. diff --git a/tools/qmlplugindump/qmltypereader.cpp b/tools/qmlplugindump/qmltypereader.cpp new file mode 100644 index 0000000000..67ba415388 --- /dev/null +++ b/tools/qmlplugindump/qmltypereader.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmltypereader.h" + +#include <QFileInfo> +#include <QFile> +#include <QRegularExpression> + +#include <iostream> + +QStringList readQmlTypes(const QString &filename) { + QRegularExpression re("import QtQuick.tooling 1.2.*Module {\\s*dependencies:\\[([^\\]]*)\\](.*)}", + QRegularExpression::DotMatchesEverythingOption); + if (!QFileInfo(filename).exists()) { + std::cerr << "Non existing file: " << filename.toStdString() << std::endl; + return QStringList(); + } + QFile f(filename); + if (!f.open(QFileDevice::ReadOnly)) { + std::cerr << "Error in opening file " << filename.toStdString() << " : " + << f.errorString().toStdString() << std::endl; + return QStringList(); + } + QByteArray fileData = f.readAll(); + QString data(fileData); + QRegularExpressionMatch m = re.match(data); + if (m.lastCapturedIndex() != 2) { + std::cerr << "Malformed file: " << filename.toStdString() << std::endl; + return QStringList(); + } + return m.capturedTexts(); +} diff --git a/tools/qmlplugindump/qmltypereader.h b/tools/qmlplugindump/qmltypereader.h new file mode 100644 index 0000000000..b995566e0b --- /dev/null +++ b/tools/qmlplugindump/qmltypereader.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLTYPEREADER_H +#define QMLTYPEREADER_H + +#include <QString> +#include <QStringList> + +QStringList readQmlTypes(const QString &filename); + +#endif // QMLTYPEREADER_H |