Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-86185

Decide on what should happen to explicit style imports

    XMLWordPrintable

Details

    • Task
    • Resolution: Done
    • P2: Important
    • 6.0
    • 6.0
    • Quick: Controls 2
    • None

    Description

      Problem

      After the recent type registration changes, explicit style imports now change which style is in use, whereas previously the only way to set a style was through one of these methods:

      https://doc.qt.io/qt-5/qtquickcontrols2-styles.html#using-styles-in-qt-quick-controls

      This results in e.g. QTBUG-86263.

      For example, consider this code:

      import QtQuick.Controls
      import QtQuick.Controls.Material
      import QtQuick.Controls.Universal
      

      In Qt 5, this would import QtQuick.Controls, which would cause this function to be called:

      https://code.qt.io/cgit/qt/qtquickcontrols2.git/tree/src/imports/controls/qtquickcontrols2plugin.cpp?h=5.15#n105

      This causes each control type to be registered for the style that was set by the user. The types are registered under the QtQuick.Controls import.

      The following imports would then cause the style plugins to be loaded, making the API of those plugins available to QML, but would not cause any control types to be registered.

      In Qt 6, this will import QtQuick.Controls, and similarly to Qt 5, cause each control type to be registered for the style that was set by the user. It does this by "aliasing" the QtQuick.Controls import to the URI of the style that was set by the user. See the qmlRegisterModuleImport() calls in the code below:

      https://code.qt.io/cgit/qt/qtquickcontrols2.git/tree/src/imports/controls/qtquickcontrols2plugin.cpp#n110

      So if the user set "MyStyle", importing "QtQuick.Controls" would be equivalent to importing "MyStyle".

      However, since the type registration logic is simplified, it means that the following imports behave like regular QML imports; they will override any types that were previously registered under the same name.

      Solutions

      Strong requirements:

      • Runtime style selection (i.e. most of the techniques described in the documentation above) must continue to work.

      Preferable:

      • It remains possible to use attached API from different styles in the same file. This avoids the need for the user to resort to file selectors, which are a hassle for such small tasks.

      Option 1: move all style APIs under a single "Style" API

      E.g.

      import QtQuick.Controls.Material
      import QtQuick.Controls.Universal
      
      // ...
      
          header: ToolBar {
              Material.foreground: "white"
              Universal.foreground: "pink"
      

      would become

      import QtQuick.Controls
      
      // ...
      
          header: ToolBar {
              Style.foreground: Style.name === "Material ? "white" : "pink"
      

      Q: What about attached properties that don't exist for the current style?

      e.g.

          Material.elevation: 1
      

      that won't work if we switch to

          Style.elevation: 1
      

      and then run the app with Universal

      A:

      You could have a free form field for extra properties:

          Style.properties: { elevation: 1 }
      

      Then, if you know you're going to run material style, you can use the direct form, or otherwise you'd use the "properties" form.

      Pros:

      • Allows similar code to old approach without resorting to file selectors.

      Cons:

      • Hurts tooling like auto-completion in Creator.
      • The "properties" part is not strongly typed.
      • Users still can't import their own styles explicitly if they have some API they need to access.

      Option 2: Find a different module name for just the attached properties

          import QtQuick.Controls.Material.Attached
      ​

      Pros:

      • Allows applications to use the same bindings as before.

      Cons:

      • Would need a different name for styles like Fusion which have a singleton for their style object.
      • It's arguable whether this is worth breaking user code (imports) for.
      • Users still can't import their own styles explicitly if they have some API they need to access.

      Option 3: Move the attached properties for the predefined styles into QtQuick.Controls.impl or similar

      Pros:

      • Allows applications to use the same bindings as before.

      Cons:

      • Applications will have QML types (attached types, singletons, etc.) for every style registered with the QML engine, even if they will never use them.
      • Users still can't import their own styles explicitly if they have some API they need to access.

      Option 4: Introduce some special QML keyword to only import specific API

      Similar to QTBUG-84794.

      import QtQuick.Controls
      import Material from QtQuick.Controls.Material
      

      This would tell the engine to only make the type named "Material" available to QML, not e.g. the control types found in the qmldir file.

      Pros:

      • Allows applications to use the same bindings as before.
      • Could have other uses outside of this, where people only want to use a certain subset of an API.

      Cons:

      • Would required a potentially large amount of work in qtdeclarative, or might not even be possible?

      Option 5: QtQuick.Controls.Material.Style

      Each built-in style could have a .Style module that provides only the QML types. Users can still import QtQuick.Controls.Material like they used to, and set the style with "Material", and we'll append ".Style" to the URI in QtQuickControls2Plugin if it's a built-in style.

      Pros:

      • Code is compatible

      Cons:

      • Users still can't import their own styles explicitly if they have some API they need to access. We could require that they split their styles up into two modules as well, but that's a lot of hassle for very little benefit to them.

      Option 6: State that style-specific API (attached objects, singletons, etc.) should only be used in file-selected files if the application supports more than one style

      See https://codereview.qt-project.org/c/qt/qtquickcontrols2/+/311798 for an example of how this would look for the gallery example.

      If the application supports only style, this wouldn't be necessary.

      Explicit style imports would then mean "use this style".

      Pros:

      • Works for user styles too.
      • No more unconditional usage of style-specific API even when that style isn't in use.

      Cons:

      • A little tedious.
      • Could confuse users: if they import a different style in two QML files, suddenly their application will be using the wrong style. We would need some way of warning users, and it would probably only work for the built-in styles.

      Option 7: Introduce some special QML keyword to only import unique API

      import QtQuick.Controls
      import unique from QtQuick.Controls.Material
      

      This would tell the engine to only import types that haven't already been registered.

      Pros:

      • Allows applications to use the same bindings as before.
      • Could have other uses outside of this, to avoid accidentally overwriting types in later imports.

      Cons:

      • Would required a potentially large amount of work in qtdeclarative, or might not even be possible?

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              mitch_curtis Mitch Curtis
              mitch_curtis Mitch Curtis
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes