I am wondering if the change was a good idea at all then. To me, it feels like the change at the root of all this was not well thought out. If it is expected that this change is going to hit so many users and they need some facility to make it easier on them than littering their code with Qt.resolvedUrl, it would perhaps have made more sense to go the other way around and introduce an unresolved URL type, which can then be resolved to a QUrl where needed without touching the default.

A separate type would not help here. You would have to change your code in order to use it. If you do so, you can as well write Qt.resolvedUrl(). Furthermore, the "string" type can already be used to store unresolved URLs today.


It feels like the support introduced for the more edge case use of unresolved URLs as the default mode has ended up causing problems for all those use cases where resolved URLs are needed. I don't _know_ what discussion there has been around that at the introduction of this change, but it _feels_ like it might be the result of a rushed in change.

We may have not made the discussion public enough, but I can assure you the decision was not rushed. There were many bug reports on the inconsistent automatic URL resolution in Qt5, and we've been trying to improve it for years. In the end it turned out that the only consistent solution is not to automatically resolve URLs. When Qt6 came around we saw the chance to do exactly that.

Usually, you set a property in the same scope as you create an element. So, all those

Image { source: "foo/bar.png" }

are unaffected. It doesn't matter if the Image element resolves the URL in its scope, or if the QML engine resolves the URL in ... the same scope. Technically, most user code does not need to change.

The case that hasn't seen enough consideration is the mechanism employed by QtQuick.Controls in some places, where the URL is exposed via an alias property. There, the scopes are in fact different and you have to resolve the URL explicitly. These places do need to change.

From my point of view, those cases still don't outweigh the abundant problems we had in Qt5.

Yet, the fact that you need to convert in some places, but not in others is not ideal. Such subtleties make it hard to reason about the code in question. The need for resolving is not apparent from the immediate context but requires examining the internals of the element that receives the URL. Furthermore, the places you need to change should be automatically detectable by tooling so that we can, for example, make qmllint show you the places where you have to resolve.

The core of the problem is our implicit conversion from string to url. At the point where we convert, we don't know whether we should resolve the url in the current context or not. We cannot know because we don't know what the URL will be used for and whether it is already absolute. The user might just store the URL to be passed somewhere else or used for further conversions. In this case we should not resolve it. Or the user might want to load some resources from the URL. In this case we should resolve it. However, if it is already absolute, it doesn't really matter.

In order to make code that uses URLs more accessible to reasoning and tooling, we _need_ the user to specify. Right now, there are three ways to specify:

- The well-known Qt.resolvedUrl(url) explicitly resolves the URL in the current context. - The new Qt.resolvedUrl(url, context) explicitly resolves the URL in the given context.
- The new Qt.url(url) explicitly doesn't resolve.

Given all this, we should eventually deprecate the implicit conversion and transition to the explicit ones. Then we can make qmllint flag every implicit string-to-url conversion and suggest the above three alternatives. I know we need to wait a minor release or two before adding a deprecation warning, but from a language perspective, this is the way to go. It will make your QML code more legible and you will immediately see where each URL is resolved.

Now, we can observe that the common case of explicitly resolving in the current context is rather verbose. Therefore, I imagine a shorthand would be useful. That's where the idea of the '@' operator comes from.

Perhaps something along these lines would be possible:

A URL in QML can be resolved or unresolved. If a URL is constructed for a string (like we have been doing for ages in QML), it is directly resolved automatically. If you need an unresolved URL, you use another syntax to construct the URL setting some flag on it to indicate it needs to be resolved at some later point using another context. Syntax could be either some constructor function (like Qt.resolvedUrl()) or perhaps as a fully fledged type `Url{url: "someUnresolvedUrl"; unresolved: true /*=default*/}`.

Mind that the URL is not resolved on _creation_ in Qt5, but rather when assigning to a property. We've actually pondered adding a "context" to QUrl itself, but in the end we've discarded the idea because it would make QUrl's comparison operators unintuitive. You'd get change signals for overwriting a URL property with a URL that looks equal, but is not.

See https://codereview.qt-project.org/c/qt/qtbase/+/369534 and https://codereview.qt-project.org/c/qt/qtdeclarative/+/369692

Furthermore, we'd re-introduce part of the original problem. What happens if you receive a URL with a context and assign it to another url property? Do you keep the original context or do you change it? Both is wrong without knowing what the user intends to do with the URL.

And, we have Qt.url() now to explicitly construct an unresolved URL.

best regards,
Ulf
_______________________________________________
Development mailing list
[email protected]
https://lists.qt-project.org/listinfo/development

Reply via email to