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

Inconsistency in popup positioning logic

    XMLWordPrintable

Details

    Description

      Code
      This code generalizes https://doc.qt.io/Boot2Qt/b2qt-customization.html#switching-between-portrait-and-landscape-views to study how to rotate GUIs that contain Popups.

      It looks like calculations relating to the top and left edges are fine, but calculations relating to the bottom and right edges are not.

      import QtQuick
      import QtQuick.Controls.Basic
      
      Window {
          id: window
          width: 400
          height: 250
          color: "beige"
          visible: true
          contentItem.rotation: rotate ? 90 : 0
          Overlay.overlay.anchors.fill: btn_rotateOverlay.checked ? container : window.contentItem
          
          property bool rotate: btn_rotateContainer.checked
          property int virtualWidth: rotate ? window.height : window.width
          property int virtualHeight: rotate ? window.width : window.height
      
          Item {
              // From https://doc.qt.io/Boot2Qt/b2qt-customization.html#switching-between-portrait-and-landscape-views
              id: container
              width: window.virtualWidth
              height: window.virtualHeight
              anchors.centerIn: parent
      
              Column {
                  anchors.centerIn: parent
                  Button {
                      id: btn_rotateContainer
                      text: "Rotate Container"
                      checkable: true
                  }
                  Button {
                      id: btn_rotateOverlay
                      text: "Rotate Overlay"
                      checkable: true
                  }
                  Button {
                      text: "Popup"
                      onClicked: popup.open()
                  }
                  Row {
                      ToolButton {
                          text: "L"
                          onClicked: drawer_left.open()
                      }
                      ToolButton {
                          text: "T"
                          onClicked: drawer_top.open()
                      }
                      ToolButton {
                          text: "R"
                          onClicked: drawer_right.open()
                      }
                      ToolButton {
                          text: "B"
                          onClicked: drawer_bottom.open()
                      }
                  }
              }
      
              Popup {
                  id: popup
                  width: parent.width/2
                  height: parent.height/2
                  anchors.centerIn: parent
                  dim: true
                  Text { text: "This way up" }
      
                  Overlay.modeless: Rectangle { color: "#AA0000FF" }
              }
      
              ComboBox {
                  model: ["Alpha", "Bravo", "Charlie"]
              }
              ComboBox {
                  model: ["Alpha", "Bravo", "Charlie"]
                  anchors.bottom: parent.bottom
              }
      
              Drawer {
                  id: drawer_left
                  Text {
                      anchors.verticalCenter: parent.verticalCenter
                      anchors.right: parent.right
                      text: "Left Drawer"
                  }
                  edge: Qt.LeftEdge
                  width: 100
                  height: window.virtualHeight
              }
              Drawer {
                  id: drawer_right
                  Text {
                      anchors.verticalCenter: parent.verticalCenter
                      anchors.left: parent.left
                      text: "Right Drawer"
                  }
                  edge: Qt.RightEdge
                  width: 100
                  height: window.virtualHeight
              }
              Drawer {
                  id: drawer_top
                  Text {
                      anchors.horizontalCenter: parent.horizontalCenter
                      anchors.bottom: parent.bottom
                      text: "Top Drawer"
                  }
                  edge: Qt.TopEdge
                  width: window.virtualWidth
                  height: 100
              }
              Drawer {
                  id: drawer_bottom
                  Text {
                      anchors.horizontalCenter: parent.horizontalCenter
                      anchors.top: parent.top
                      text: "Bottom Drawer"
                  }
                  edge: Qt.BottomEdge
                  width: window.virtualWidth
                  height: 100
              }
          }
      }
      

       

      Steps to reproduce

      1. Build and run this code
      2. Open the Popup, 2 ComboBoxes, and 4 Drawers
      3. Enable "Rotate Container" (but not "Rotate Overlay") and repeat Step #2
      4. Enable both "Rotate Container" and "Rotate Overlay" and repeat Step #2

       

      Outcomes

      • If we rotate the container only without rotating the Overlay: The dimmers, ComboBox drop-downs, and Drawers are all positioned/sized incorrectly.
      • If we rotate both the container and the Overlay:
        • The dimmers, top ComboBox drop-down, and top+left Drawers are now positioned/sized correctly.
        • However, the bottom ComboBox drop-down and the right+bottom Drawers are still wrong.

      (see attached diagram for an overview)

       

      Analysis
      These outcomes shows that the Popup positioning calculations are partially based on the Overlay. However, other parts (namely the calculations that try to keep the Popups within the right/bottom bounds) are based on the Window geometry instead. All calculations should be based on the same reference.

      https://github.com/qt/qtdeclarative/blob/v6.7.0-beta3/src/quicktemplates/qquickpopuppositioner.cpp#L126-L129

      // QQuickPopupPositioner::reposition()
      const QMarginsF margins = p->getMargins();
      QRectF bounds(qMax<qreal>(0.0, margins.left()),
                    qMax<qreal>(0.0, margins.top()),
                    p->window->width() - qMax<qreal>(0.0, margins.left()) - qMax<qreal>(0.0, margins.right()),
                    p->window->height() - qMax<qreal>(0.0, margins.top()) - qMax<qreal>(0.0, margins.bottom()));
      

      https://github.com/qt/qtdeclarative/blob/v6.7.0-beta3/src/quicktemplates/qquickdrawer.cpp

      // QQuickDrawerPositioner::reposition()
      switch (drawer->edge()) {
      case Qt::LeftEdge:
          popupItem->setX((position - 1.0) * popupItem->width());
          break;
      case Qt::RightEdge:
          popupItem->setX(window->width() - position * popupItem->width());
          break;
      case Qt::TopEdge:
          popupItem->setY((position - 1.0) * popupItem->height());
          break;
      case Qt::BottomEdge:
          popupItem->setY(window->height() - position * popupItem->height());
          break;
      }
      

       

      Other observations
      The fix for QTBUG-80910 (which was picked back to Qt 6.6) now takes the rotation into account and allows Drawers to be dragged in from the correct rotated edge. This fix is not too useful if the Drawer is positioned iscorrectly.

       

      Also, we could exploit QTBUG-123114 to "fix" the calculations for the bottom ComboBox...

      contentOrientation: rotate ? Qt.LandscapeOrientation : Qt.PrimaryOrientation
      

      ...but two wrongs don't make a right. This hack also has no effect on the bottom/right Drawers.

      Attachments

        Issue Links

          For Gerrit Dashboard: QTBUG-122963
          # Subject Branch Project Status CR V

          Activity

            People

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

              Dates

                Created:
                Updated:

                Gerrit Reviews