aboutsummaryrefslogtreecommitdiffstats
path: root/src
Commit message (Collapse)AuthorAgeFilesLines
...
* doc: demystify QSGOpenGLTexture::fromNativeExternalOES()Shawn Rutledge2021-04-131-2/+3
| | | | | | | | | | | Plausible explanation comes from https://stackoverflow.com/questions/25618977/how-to-render-to-a-gl-texture-external-oes Amends c2c180e4ee58f8cfc104207b3b56e83ddcb7e79a Pick-to: 6.1 Change-Id: I9baba2119ea85823cda829d32859c4e45f757d09 Reviewed-by: Laszlo Agocs <[email protected]>
* QQmlJSImportVisitor: add find-or-create version of enterEnvironmentAndrei Golubev2021-04-132-3/+35
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | It seems that in cases of grouped and attached properties we don't want to create a new scope every time, but instead reuse the created one, falling back to create if the scope doesn't exist Consider: ```qml QtObject { myGroup.property1: 42 // case 1 myGroup.property2: "hello" // case 2 MyAttached.property1: 42 // case 3 MyAttached.property2: "hello" // case 4 } ``` The intuition tells that cases 1 and 2 should use the same shared scope "myGroup" and so should cases 3 and 4 both use the same "MyAttached". Creating a new scope each time for attached and grouped properties seems erroneous As a drive-by, add attached property type to tst_qmllint and test that this still works after the changes to the related code Change-Id: Ib1ae85b404a7954a3a3211cbd267c414b98ac59d Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Ulf Hermann <[email protected]>
* QQmlJSImportVisitor: put name setting logic into a functionAndrei Golubev2021-04-131-4/+14
| | | | | | | | This would make the logic more visible for the reader (as it's in a separate function now) and also allow reuse Change-Id: I9a9aeaaab65163b7b5161fe9f793bdf0b8e7b554 Reviewed-by: Fabian Kosmale <[email protected]>
* QV4::EngineBase: Remove pragma packFabian Kosmale2021-04-131-7/+0
| | | | | | | | | | | This was only used on a subset of our compilers, and caused issues with pointer alignment. We have static_assert's in place to verify that the struct members are in the correct place, and pack had no effect anyway after we added the alignment padding to the struct. Change-Id: I162664ec01d4b5d97ac800afc354d0ab2e04de42 Reviewed-by: Ulf Hermann <[email protected]> Reviewed-by: Andrei Golubev <[email protected]>
* Check also property type namesEvgeniy A. Dushistov2021-04-132-6/+18
| | | | | | | | | | At the moment, qmllint only check the types of objects and classes being imported, it totally ignored types of properties that should be also imported. Fixes: QTBUG-92449 Change-Id: Ia5173bed84640d93fc48f6239179604ceb5db2cd Reviewed-by: Ulf Hermann <[email protected]>
* QQmlJSLexer: Add support for peeking one QCharFabian Kosmale2021-04-132-0/+9
| | | | | | | | | This is helpful for the upcoming handling of ?., which needs to be treated as a single token (T_QUESTION_DOT), unless it is followed by a number literal. Change-Id: Id5e992bd037c2df88ef6e66905ec58a39bb67d73 Reviewed-by: Ulf Hermann <[email protected]>
* Do not batch lines with > 1 width in alpha passLaszlo Agocs2021-04-121-1/+5
| | | | | | | | | | | Cannot possibly do reasonable overlap checks when we have no idea how such lines are rasterized, meaning we do not know the real bounds of the geometry. Pick-to: 6.1 6.0 5.15 Fixes: QTBUG-91749 Change-Id: Ia444232330da2f1d29841589f0e65bb52822c4ae Reviewed-by: Eskil Abrahamsen Blomfeldt <[email protected]>
* QV4EngineBase: Do not create zero-sized array on 32 bit platformsFabian Kosmale2021-04-121-1/+3
| | | | | | | | | | | Zero-sized arrays are a non-standard extension, and do not work on MSVC. We can instead conditionally add an explicit padding member on platforms where POINTER_SIZE == 8. Fixes: QTBUG-92562 Pick-to: 6.1 6.1.0 Change-Id: I8462eb05e16c42045c0c95f026321c6e20e5c6bb Reviewed-by: Maximilian Goldstein <[email protected]>
* Fix typo in local variable in QQmlJSImportVisitorAndrei Golubev2021-04-121-2/+2
| | | | | Change-Id: I4374948729a4b6d6d273bd1f0aab156a94ae7c4a Reviewed-by: Fabian Kosmale <[email protected]>
* Remove QString overload forwarding to QStringViewFabian Kosmale2021-04-091-7/+0
| | | | | | Change-Id: Ia97aefc3dbedae1e979db93638836b366eee153f Reviewed-by: Qt CI Bot <[email protected]> Reviewed-by: Ulf Hermann <[email protected]>
* Remove unnecessary int() casting in QRandomGenerator::boundedThiago Macieira2021-04-091-1/+1
| | | | | | | | | | Commit 21d39168170c6833512c4a5f53985272741bd7e7 in qtbase added the 64-bit version, so qsizetype now works cross-platform. The casts were added in commit aef0aac581fbbead07be939873e34045137b94ff to make qtdeclarative compile. Change-Id: I26b8286f61534f88b649fffd166c409c5c232230 Reviewed-by: Lars Knoll <[email protected]>
* qmllint: add options for setting logging levelsMaximilian Goldstein2021-04-092-17/+73
| | | | | | | Add the ability to individually disable and set the severity of all warnings produced by qmllint. Change-Id: I46081f8b37fb90f8d0f4a5f2f08223d7b7285041 Reviewed-by: Fabian Kosmale <[email protected]>
* Do not auto-clean components with live inline componentsUlf Hermann2021-04-091-1/+11
| | | | | | | | | | The inline components do not hold a strong reference to their outer type because that would be a reference cycle. Fixes: QTBUG-92236 Pick-to: 5.15 6.0 6.1 Change-Id: I6d76a114352653210f0ece6c198cf761d3b4eda1 Reviewed-by: Fabian Kosmale <[email protected]>
* When resolving property types, also update the property type namesUlf Hermann2021-04-071-3/+11
| | | | | | | | | Otherwise we end up with a mixture of QML and C++ names. Pick-to: 6.0 6.1 Task-number: QTBUG-92447 Change-Id: I94c44307d8dd762d11cfd8f178f33ab6a895ee83 Reviewed-by: Fabian Kosmale <[email protected]>
* Resolve inline components before root componentFabian Kosmale2021-04-073-38/+115
| | | | | | | | | | | | | | | | | | | | | | | During instantiation of a component, we might need access to the meta-objects of its referenced objects in the QQmlPropertyCacheCreator. In the case of inline components, those might have however not been complete: If they contained alias properties (which change the shape of the metaobject), we would not be aware of it, as those are only resolved when QQmlComponentAndAliasResolver has run. QQmlComponentAndAliasResolver runs however necessarily after the QQmlPropertyCacheCreator, as the alias resolver needs the metaobject to know about the non-alias properties. We fix this by restructuring the passes, so that we first run both passes for each inline component, and only afterwards for the component containing them. This is done by making QQmlPropertyCacheCreator resumable: resumableBuildMetaObjects processes one (inline) component at a time, and remembers enough state to continue. Fixes: QTBUG-91143 Fixes: QTBUG-85980 Change-Id: Ib0d0bc5ff6cf067b2443b2e0cfb5400a9bd96754 Reviewed-by: Ulf Hermann <[email protected]>
* Run QQmlDefaultPropertyMerger after alias resolverFabian Kosmale2021-04-071-5/+5
| | | | | | | | | There is no dependency between them. This is done in preparation of a restructuring of the PropertyCacheCreator and AliasAndComponentResolver passes. Change-Id: I2695435ca9b2aed5c4c34c281cd1611f5373e3f8 Reviewed-by: Ulf Hermann <[email protected]>
* Don't run into UB when converting doubles to int on property assignmentUlf Hermann2021-04-061-1/+1
| | | | | | | | | We cannot just do a straight cast in this case. The floor()/ceil() toInteger() does actually has no effect if the value overflows. We need to do a full JavaScript coercion in order to be safe. Change-Id: I122974de5f0b9bdba9133b8503e328c4f7185934 Reviewed-by: Fabian Kosmale <[email protected]>
* CMake: Disable JIT for arm64 when doing macOS universal buildsAlexandru Croitor2021-04-037-3/+46
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Our current approach to building universal macOS Qt is to pass 2 -arch flags to clang, which underneath spawn 2 clang invocations with each separate arch and lipo-s the result together. This approah also meanss that we do only one set of config tests for the main (first) architecture. Currently Qml doesn't support JITing for macOS on Apple Silicon (arm64), but if the first architecture is x86_64, the qml_jit feature will be set to 'true', and cause compilation errors when trying to build the arm slice of the jit source files. To circumvent that, and allow skipping compilation of JIT specific code, we have to apply the same trick we do in qtbase, which is to set a compile definition that takes the current architecture into account, and surround all relevant code with an #if block taking to account both the feature and current architecture. Use a custom hacky qt_extra_definition call to redefine the value of QT_FEATURE_qml_jit based on the original feature value and the current architecture. Additionally, surround the jit source files with #if QT_CONFIG(qml_jit). Amends 561a2cec9b95b22783a00b48078b532010357066 Task-number: QTBUG-85447 Change-Id: I28b286d218333076223177c456175f180888a667 Reviewed-by: Ulf Hermann <[email protected]>
* Optimize invocation of signal handlersUlf Hermann2021-04-013-11/+27
| | | | | | | | | We don't have to do any argument conversion if there are no arguments. Furthermore, we don't need to do the metatype-to-JS conversion if the handler to be invoked is AOT-compiled. Change-Id: I03d8fd7ad07d311d64c39adfd39febbe94396d2f Reviewed-by: Fabian Kosmale <[email protected]>
* QV4QObjectWrapper: Store the whole signalFabian Kosmale2021-04-013-9/+10
| | | | | | | | | | | | | | | | | | | | | 90be89d771425044a84e9e79e4e668e065acc825 changed the connection logic to actually pass the receiver to connect in order to fix disconnect cleanup. However, we omitted to change QObjectSlotDispatcher::impl accordingly. The previous logic was: - store the index of the signal in signalIndex - In impl, in the call case, we would get passed the emitting object (sic!) as the receiver parameter. Then we would use the object and the signal index to obtain the QMetaMethod. - From the QMetaMethod, we could get the signal's number of parameters. After the aforementioned change, that does not work anymore: The receiver is now the actual receiver of the signal, thus we get the wrong method, and potentially the wrong number of parameters. To fix this, we now store the complete QMetaMethod of the signal. Pick-to: 6.1 Change-Id: I868c51edf24a61d14eaf958ed7942da27f54a5c3 Reviewed-by: Ulf Hermann <[email protected]>
* Simplify populateJSCallArguments a bitUlf Hermann2021-04-011-4/+2
| | | | | Change-Id: I7e2f09c3ddf4a855c87c36a16b9f48af3dadf978 Reviewed-by: Fabian Kosmale <[email protected]>
* Un-special case QQmlPropertyValueTypeFabian Kosmale2021-03-314-28/+7
| | | | | Change-Id: I5123f72fea4198505c27678dbaaea26313b2327c Reviewed-by: Ulf Hermann <[email protected]>
* Move type registration into their own headerFabian Kosmale2021-03-313-96/+143
| | | | | | | | | | | For now, this simply gives us a more lightweight header than qqml.h. In the future, we could move this completely out of QML, to ease optional QML integration in libraries that do not want to directly depend on QtQml. Task-number: QTBUG-92258 Change-Id: I3583ce85905ed8fa0a45abb360728178c774d679 Reviewed-by: Ulf Hermann <[email protected]>
* QQuickItem: Do not dereference nullptrFabian Kosmale2021-03-311-1/+2
| | | | | | | | | | During engine shut-down, the delivery agent might already have been deleted, but we still get a call to setEffectiveVisibleRecur. Thus we need to check that the agent is non-null. Pick-to: 6.1 Change-Id: Ie2d5923d686789c0758913799ff0702a85a55402 Reviewed-by: Ulf Hermann <[email protected]>
* Do not throw an exception in the optimized AOT lookup pathUlf Hermann2021-03-311-5/+1
| | | | | | | | | | | | The method already returns a bool status which tells the caller "something is wrong". If something is wrong, the caller will try a different method to retrieve the value, and that will generate the exception again. We should not shortcut the procedure as generating the exception requires the instruction pointer to be set, which is additional overhead on the caller's side. Change-Id: Ia2d3a90c897b14660a18a5740498a0e5016dc49a Reviewed-by: Fabian Kosmale <[email protected]>
* Fix missing 'We mean it' warning for generated qml_compile_hash_p.hKai Köhne2021-03-311-0/+15
| | | | | | Pick-to: 6.1 Change-Id: I327d377c80749741274a76dfa609417c005bf6c7 Reviewed-by: Alexandru Croitor <[email protected]>
* Android: Add possibility to copy text from read-only Qml TextEditPiotr Mikolajczyk2021-03-313-37/+48
| | | | | | | | | | | | In case of a read-only TextEdit it was imposibble to move text selection handles and copy the selected text from. You could see the handles but you couldn't move them, neither you could copy the text. Fixes: QTBUG-75556 Change-Id: I23e9a1948d01b5078046fe33559d13949bbde5d5 Reviewed-by: Rami Potinkara <[email protected]> Reviewed-by: Ville Voutilainen <[email protected]>
* Use CLASS_NAME rather than CLASSNAME for qt6_add_qml_module()Craig Scott2021-03-3118-37/+63
| | | | | | | Task-number: QTBUG-88763 Change-Id: I118227ec418ac59eb6603d236b38b1a4319a51c2 Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Ulf Hermann <[email protected]>
* CMake: Account for qtbase refactoring in qt_internal_add_plugin()Craig Scott2021-03-311-15/+44
| | | | | | | | | Task-number: QTBUG-88763 Pick-to: 6.1 Change-Id: I66c22f876be66ca64cd364f574a6f3e862e4abf5 Reviewed-by: Ulf Hermann <[email protected]> Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Andrei Golubev <[email protected]>
* QQmlJSLogger: Don't store references in a classUlf Hermann2021-03-311-3/+2
| | | | | | | | | This is rather dangerous since we often pass references to stack values there. Those can easily disappear. Change-Id: I5a57b4af9035ba032b5fd475aed6af29aa888d83 Reviewed-by: Ulf Hermann <[email protected]> Reviewed-by: Fabian Kosmale <[email protected]>
* qmllint: Improve import warningsMaximilian Goldstein2021-03-306-11/+34
| | | | | | | | | | | Import warnings will now always be clearly associated with the line and import that caused them. They're also visually separated now and don't redundantly show the importing files name in every warning. Some type of imports such as file or URI based imports never had their warnings processed which is also fixed by this change. Change-Id: I63d720fcf198ff5302c2566a91cde4c716697f7e Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Andrei Golubev <[email protected]> Reviewed-by: Ulf Hermann <[email protected]>
* Consistent naming for (to|from)Is(Defined|Sourced) in animationsAndreas Buhr2021-03-306-41/+40
| | | | | | | | | | | | | | | | | To store whether "from" and "to" values are defined and/or sourced, QQuickPathAnimationUpdater used fromDefined format, QQuickAbstractAnimationPrivate used fromIsDefined format, QQuickAnimationPropertyUpdater used fromIsDefined format, QQuickAnimatorPrivate used isFromDefined format, QQuickBulkValueAnimator used fromDefined format, QQuickAnimationPropertyUpdater used fromDefined format. This patch changes all these to use the variable names "fromIsDefined", "fromIsSourced", and "toIsDefined". This makes the code more readable. Pick-to: 6.0 5.15 Change-Id: Ia6c228208eb651247b0ba70f83afadb5b1027049 Reviewed-by: Ulf Hermann <[email protected]>
* Prevent CMP0116 warning with CMake 3.20 or laterCraig Scott2021-03-311-9/+16
| | | | | | | | | | | | | | | | CMake 3.20 introduced a change in the handling of relative paths in *.d depfiles referenced by custom commands. To avoid a CMP0116 policy warning, we have to explicitly set the policy to NEW and also change the relative path(s) we embed in the depfiles to be relative to CMAKE_CURRENT_BINARY_DIR rather than the top level binary directory. Fixes: QTBUG-92026 Pick-to: 6.1 6.0 Change-Id: I1a84d29f1a8d5c48bae5bc11596806f1e0e07919 Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Andrei Golubev <[email protected]> Reviewed-by: Ulf Hermann <[email protected]> Reviewed-by: Alexandru Croitor <[email protected]>
* Add QDeferredSharedPointer::get()Andrei Golubev2021-03-301-0/+1
| | | | | | | Because QSharedPointer (and almost any other smart pointer) provides it Change-Id: I7e0cf0d700ed72de56b6949ee18b3469881088b3 Reviewed-by: Fabian Kosmale <[email protected]>
* QQuickWindow::deliveryAgentPrivate: check for nullptrFabian Kosmale2021-03-301-1/+1
| | | | | | | | | The deliveryAgent pointer can be null. In that case, we must not call get on it, as get requires a non-null pointer. Change-Id: I9ca436c0be3c2f9196a2343d9ecb31e3bc6614d7 Reviewed-by: Andrei Golubev <[email protected]> Reviewed-by: Volker Hilsheimer <[email protected]>
* QV4CompilerScanFunctions: Silence code checker warningFabian Kosmale2021-03-302-3/+8
| | | | | | | | | | | | The AST is constructed in such a way that having exportAll set always implies that a fromClause exists. Also, clean up exportAll: We do not need a separate member to track it. Its value is fully determined by the presence of fromClause and exportsClause. Thus, replace it with a function. Change-Id: Ib7db2bbaf326ecc36a7f4a2986a7c1fb54db6cd5 Reviewed-by: Andrei Golubev <[email protected]> Reviewed-by: Ulf Hermann <[email protected]>
* Add QQmlJSMetaProperty::isValid()Andrei Golubev2021-03-301-0/+2
| | | | | | | *Method and *Enum already have one, so *Property should have one also Change-Id: Ia3a7b2a082f88296ee97af7343c68c5529d5220f Reviewed-by: Fabian Kosmale <[email protected]>
* QQmlMetaTypeData: Purge custom string convertersFabian Kosmale2021-03-307-67/+4
| | | | | | | | | Those were never exposed anyway. color, date, etc. are handled by the less generic converters in qqmlstringconverters_p.h. Change-Id: I43a94acda08344de742440dd3b956a7077096b11 Reviewed-by: Ulf Hermann <[email protected]> Reviewed-by: Andrei Golubev <[email protected]>
* Adjust to metaobject revision changeFabian Kosmale2021-03-301-1/+3
| | | | | | | | | | And make sure that we notice that at compile time the next time it happens. Change-Id: I552481e0f9f7178ec3f1450d8050ae9434894e5a Reviewed-by: Maximilian Goldstein <[email protected]> Reviewed-by: Ulf Hermann <[email protected]> Reviewed-by: Andrei Golubev <[email protected]>
* qv4qobjectwrapper_p.h: Work around MinGW bugFabian Kosmale2021-03-301-0/+2
| | | | | | | | | | mingw 8.1 does not handle the access to privates in a SFINAE context correctly. Publically expose MultiplyWrappedQObjectMap::value_type to work around this issue. Change-Id: I9cddeaca8ebaaa77a786fac2c4e210dd3b852d4d Reviewed-by: Andrei Golubev <[email protected]> Reviewed-by: Ulf Hermann <[email protected]>
* qmljslogger: Disable copying and add a default constructorMaximilian Goldstein2021-03-301-1/+2
| | | | | | | Stick to the rule of three to make our code checker happy. Change-Id: Ifc78770aec96ac2a0296197f88b3faf8ff690eae Reviewed-by: Ulf Hermann <[email protected]>
* DelegateModelGroup: Fix bug where item could be removed from the modelJan Arve Sæther2021-03-291-9/+93
| | | | | | | | | | | | | | | | | | | | | | If an item was removed from the DelegateModelGroup before it was completed it caused subsequent items in the model to be missing in some cases. The reason was that while populating the ListView, it iterated with an index for each item to call createItem() on. However, createItem() might call onCompleted (which in the case of QTBUG-86708 removed the item from the DelegateModel), which caused the next index we called createItem() with to be wrong (it became one step ahead). We therefore add a helper class MutableModelIterator, which keeps track of if a index in the model got removed (and if the iterator index needs to be adjusted because of that).... Pick-to: 6.1 6.0 5.15 Task-number: QTBUG-86708 Change-Id: I33537b43727aed4f2b9bdda794b011b6684c44b4 Reviewed-by: Richard Moe Gustavsen <[email protected]>
* QQuickGraphicsInfo: Fix CodeChecker warning about nullptr dereferenceFabian Kosmale2021-03-291-2/+4
| | | | | | | | | The item passed to QQuickGraphicsInfo can be null. In that case, calling its window method is ill-advised. As a drive-by, convert the old-style connect to the "modern" variant. Change-Id: Ia135be52c8132d83c1f19a7bab76b1dcffa81dac Reviewed-by: Volker Hilsheimer <[email protected]>
* qmlcompiler/qmllint: Use unified loggerMaximilian Goldstein2021-03-298-34/+723
| | | | | | | | | | Improves the logging situation greatly by allowing all logging messages to be assigned different severities, highlighting the code that caused them and by now ensuring a qmllint warning will always result in a non-zero exit code. A later patch will expose more of these options to the user. Change-Id: Id9b036fe3ba80dd18e9f8cb1b05efa891713d5a8 Reviewed-by: Ulf Hermann <[email protected]>
* Avoid needless construction and destruction of return valuesUlf Hermann2021-03-294-14/+15
| | | | | | | | | | In most cases the AOT compiled function will successfully placement-new the return value. Therefore, we can provide uninitialized space. Only do the construct/destruct dance in the cases when it's already slow. Change-Id: Ia339774fde03e459f290f167ddadd1c47a644b8e Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Andrei Golubev <[email protected]>
* Provide an AOT-optimized code path for binding evaluationUlf Hermann2021-03-294-62/+213
| | | | | | | | | We don't need to convert the results of calling binding functions back and forth. Change-Id: Icb997532ebc3092f3e3882a804173346b450e435 Reviewed-by: Andrei Golubev <[email protected]> Reviewed-by: Fabian Kosmale <[email protected]>
* Optimize some common lookups for AOT compiled codeUlf Hermann2021-03-292-0/+73
| | | | | | | | Getting a property of an object and getting an object by ID should not require any type transformations. Change-Id: I7ba06cf7c09cdf4ce7b0093b670fd7879ecf799a Reviewed-by: Fabian Kosmale <[email protected]>
* Allow a bare lookup capture, without executing the lookupUlf Hermann2021-03-292-0/+55
| | | | | | | | This is useful when we know how to read the property directly. Change-Id: Ibc314a8e8bf9e51b3a15d934f6a1f17a53ea168a Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Andrei Golubev <[email protected]>
* doc: fix up QQuickItem::contains() docsShawn Rutledge2021-03-261-12/+13
| | | | | | | | | | | | - \c true and \c false - indentation - mention containmentMask() - overwritten -> overridden - hit-testing is for all pointing devices, not just the mouse Change-Id: I1debe1f0b3a4f729225c462b20dd10bc4e1cf8b0 Reviewed-by: Richard Moe Gustavsen <[email protected]> Reviewed-by: Paul Wicking <[email protected]>
* QQuickTableView: always update content size when rebuilding small tablesRichard Moe Gustavsen2021-03-262-2/+26
| | | | | | | | | | | | | | | | | | | | If you have a TableView with only a couple of rows, and you add a third one, the contentHeight doesn't update. This is fine if not all rows are loaded (some are outside the viewport), but when they are all inside, it should update to reflect the exact height. The same is also the case for the contentWidth. If you add a new row that increases the with of a column (and all columns are visible), the contentWidth should update. This patch adds an extra check when we do a rebuild (which we do when you add a new row), to see if all rows or columns are loaded. And if that is the case, we update contentHeight or contentWidth, respecitively. Pick-to: 6.1 6.0 5.15 Fixes: QTBUG-92099 Change-Id: I806bfb7c3606fca97c5d27cbb91856cc40df9fb8 Reviewed-by: Mitch Curtis <[email protected]>