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

Improve documentation and API for transforming Qt Quick window content

    XMLWordPrintable

Details

    Description

      (Here, "transforming" refers to both geometrical transformations as well as resizing)

      Users might want to transform a window's contents, e.g.:

      • To enlarge the contents of the whole window, or
      • To implement an HMI on an embedded system where the device display has a different orientation than the UI orientation, for example when using a landscape GUI on a portrait display panel

       

      Currently, it difficult for a user to transform the contents of a Qt Quick window because of insufficient/incomplete documentation and suboptimal API. 

      What our documentation says so far

      https://doc.qt.io/Boot2Qt/b2qt-customization.html#switching-between-portrait-and-landscape-views shows a simple example that rotates a "container" item and swapping its width and height:

      import QtQuick
      
      Item {
          id: root
          width: 800
          height: 1280
      
          Rectangle {
              width: root.height
              height: root.width
              rotation: 90
              anchors.centerIn: parent
      
              // [Content here gets rotated]
          }
      }
      

      Unfortunately, this example is insufficient when the application uses Qt Quick Controls that involve Popups (e.g. ComboBox drop-downs, Menus, Drawers, Dialogs). Users will find that their content inside Rectangle are generally transformed as expected, but many Popup elements are not.

       

      https://doc.qt.io/qt-6/qml-qtquick-controls-popup.html#popup-sizing clarifies things a bit more: "The popup's content item gets parented to the overlay, and does not live within the popup's parent". It explains that we need to apply the transformation both to the user-implemented Item tree and to the Overlay:

      import QtQuick
      import QtQuick.Controls.Basic
      
      Window {
          width: 800
          height: 600
          visible: true
      
          Scale {
              id: scale
              xScale: 2.0
              yScale: 2.0
          }
          ComboBox {
              transform: scale
              model: ["Alpha", "Bravo", "Charlie"]
          }
          Overlay.overlay.transform: scale
      }
      

      Unfortunately, this example is insufficient when the application uses ApplicationWindow instead of Window. Users will find that their content and the Popup elements are transformed as expected, but the window's background is not.

       

      There's no documentation to explain that one. And how do we know if anything else is missing? To get the answers, it is helpful to know some of the internal details of Qt Quick's window classes.

      Three Types of Windows

      Putting these details to good use for geometric transformations

      From the diagram, we can see that, if there is no unscaled content in the window, then the ComboBox example above can be simplified by transforming the QQuickRootItem directly...

      import QtQuick
      import QtQuick.Controls.Basic
      
      Window {
          width: 800
          height: 600
          visible: true
          contentItem.transform: Scale { // or contentItem.parent.transform, if using ApplicationWindow
              xScale: 2.0
              yScale: 2.0
          }
          ComboBox {
              model: ["Alpha", "Bravo", "Charlie"]
          }
      }
      

      ...which would auto-transform all relevant sub-trees (including ApplicationWindow.background if it exists). The only minor obstacle is that ApplicationWindow.contentItem != Window.contentItem (QTBUG-123494) which can trip up unsuspecting users. Documentation can help here, but more intuitive API would be better.

       

      Problem solved? Not yet, because although we can apply geometric transformations to QQuickRootItem, we cannot resize it. So, window rotations need a lot more boilerplate to work.

       

      Putting these details to good use for swapping screen orientation

      Window

      It would be nice and easy if we could do the same as the Scaling example above, and simply rotate+resize the QQuickRootItem. But...

      Obstacles, or things that are unclear

      • While we can rotate the QQuickRootItem, we cannot resize it. So we must resize its children individually instead.
      • QQuickOverlay resists attempts to set its width and height (QTBUG-122962). Fortunately, anchoring works.

       

      Solution
      Putting that together, we get:

      import QtQuick
      import QtQuick.Controls.Basic
      
      Window {
          id: root
          width: 640
          height: 480
          visible: true
      
          contentItem.rotation: 90
          Overlay.overlay.anchors.fill: gui
      
          MyGui {
              id: gui
              width: root.height
              height: root.width
              anchors.centerIn: parent
          }
      }
      

      QQuickView/QQuickWidget

      From the diagram above, we can see that the QQuickView/QQuickWidget structure is quite similar to Window, but...{}

      Obstacles, or things that are unclear

      • The top-level user Item's size is tied to the QQuick(View|Widget)'s size, so we cannot resize it. So we must resize its child instead.
      • That child is not a sibling of the QQuickOverlay, which blocks us from setting up anchors. So we must re-parent it.
      • The QQuickRootItem and QQuickOverlay are not available when Component.onCompleted is triggered (QTBUG-122894). So we must defer assignments/bindings.

       

      Solution
      Putting that together, we get:

      import QtQuick
      import QtQuick.Controls.Basic
      
      Item {
          id: root
          width: 640
          height: 480
          visible: true
      
          Component.onCompleted: Qt.callLater(function(){
              let windowContentItem = Overlay.overlay.parent // This does not exist at Component.onCompleted
              gui.parent = windowContentItem // Reparent the MyGui to allow the Overlay to anchor to it
              windowContentItem.rotation = 90
              Overlay.overlay.anchors.fill = gui
          })
      
          MyGui {
              id: gui
              width: root.height
              height: root.width
              anchors.centerIn: parent
          }
      }
      

      ApplicationWindow

      From the ApplicationWindow diagram above, we can see that ApplicationWindow's structure is quite different from Window's. This, along with a few other important details, are not readily apparent from our documentation...

      Obstacles, or things that are unclear

      • ApplicationWindow.contentItem != Window.contentItem (QTBUG-123494). So to access the QQuickRootItem, we must use contentItem.parent or Overlay.overlay.parent
      • The top-level user Item is not a sibling of the QQuickOverlay, which blocks us from setting up anchors. However, we can take advantage of the fact that the background is a sibling instead.
      • The QQuickContentItem is also a direct child of the QQuickRootItem. So we have a 3rd thing to resize compared to the earlier cases (plus we must make room for the menuBar/header/footer if they exist).

       

      Solution
      Putting that together, we get:

      import QtQuick
      import QtQuick.Controls.Basic
      
      ApplicationWindow {
          id: root
          width: 640
          height: 480
          visible: true
      
          background: Item {
              anchors.centerIn: parent
              width: root.height
              height: root.width
          }
          contentItem {
              parent.rotation: 90
              anchors.fill: background
              anchors.topMargin: (root.menuBar?.height ?? 0) + (root.header?.height ?? 0)
              anchors.bottomMargin: (root.footer?.height ?? 0)
          }
          Overlay.overlay.anchors.fill: background
      
          MyGui {
              id: gui
              anchors.fill: parent
          }
      }
      

      Assessment of the current situation

      • The code needed to rotate ApplicationWindow is very different from the code needed to rotate Window.
      • Coming up with the solutions above required knowledge about the internal, undocumented details of the different window classes' item trees. Users cannot be generally expected to come up with these solutions themselves.
      • It is not guaranteed that these solutions will continue to work for future versions of Qt.

      Next Steps

      1. Documenting these solutions could be a good first step. However, it is not ideal that we are relying on implementation details of the windows' structure.
      2. It would be nicer in the long run to have a formal API to apply the transformations - and importantly, this API should be consistent for both Window and ApplicationWindow. (I understand that making it work seamlessly with QQuickView/QQuickWidget is less feasible)

      Some ideas for the API

      The core idea is to have a designated item to receive all transformations, and have it propagate to all parts of the scene graph (both user-implemented and internal)

      • Decouple the QQuickRootItem's size from the window's size?
        • If we can make QQuickRootItem's width/height different from the window's width/height, then most of the boilerplate code above for rotating content will become unnecessary.
        • If we don't want to allow arbitrary sizes, perhaps there can be a property that swaps width and height instead
      • Insert a new container item between the QQuickRootItem and its current children?
        • If it is not feasible/desirable to decouple the QQuickRootItem's size from the window's size, then perhaps we could have a "sub-root" item instead, which is the single point of applying the transformations.

      Other considerations

      Qt for MCUs 2.7 just introduced a system for rotating the display (https://doc.qt.io/QtForMCUs-2.7/qtul-staticscreenrotation.html ). It would be good if the system for "regular" Qt and MCUs are as similar as possible.

      Attachments

        Issue Links

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

          Activity

            People

              qt.team.quick.subscriptions Qt Quick and Widgets Team
              skoh-qt Sze Howe Koh
              Votes:
              1 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

                Created:
                Updated:

                Gerrit Reviews

                  There are no open Gerrit changes