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

Focus handling incorrect in a widget hierarchy with Qt::WA_PaintOnScreen and Qt::WA_DontShowOnScreen

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • P3: Somewhat important
    • None
    • 5.8.0
    • None

    Description

      The following widget hierarchy has two widgets which indicate whether they are currently focused (blue frame), as well as whether the left mouse button is currently pressed down (red frame):

      #include <QtWidgets>
      
      class HighlightFocusWidget : public QWidget
      {
      public:
          HighlightFocusWidget(QWidget* parent)
              : QWidget(parent)
              , m_mouseDown(false)
          {
              setAutoFillBackground(false);
              setFocusPolicy(Qt::WheelFocus);
      
              connect((QApplication*)QApplication::instance(), &QApplication::focusChanged, this, [this]() { update(); });
          }
      
      protected:
          virtual void paintEvent(QPaintEvent* event) override
          {
              QPainter painter(this);
              painter.setPen(Qt::NoPen);
              QColor color = (this == QApplication::focusWidget()) ? QColor(Qt::blue) : palette().color(QPalette::Background);
              painter.setBrush(color);
              painter.drawRect(rect());
      
              if (m_mouseDown)
              {
                  painter.setBrush(Qt::red);
                  painter.drawRect(rect().adjusted(4, 4, -4, -4));
              }
          }
      
          virtual void mousePressEvent(QMouseEvent* event) override
          {
              if (event->button() == Qt::LeftButton)
              {
                  m_mouseDown = true;
                  update();
              }
          }
      
          virtual void mouseReleaseEvent(QMouseEvent* event) override
          {
              if (event->button() == Qt::LeftButton)
              {
                  m_mouseDown = false;
                  update();
              }
          }
      
          virtual void contextMenuEvent(QContextMenuEvent *event) override
          {
              QMenu menu(this);
              menu.addAction("Menu Entry");
              menu.exec(event->globalPos());
          }
      
      private:
          bool m_mouseDown;
      };
      
      class PaintOnScreenWidget : public QWidget
      {
      public:
          PaintOnScreenWidget()
          {
              setAttribute(Qt::WA_PaintOnScreen);
          }
      
      protected:
          virtual QPaintEngine* paintEngine() const override
          {
              return 0;
          }
      };
      
      int main(int argc, char *argv[])
      {
          QApplication app(argc, argv);
      
          QWidget* w = new QWidget();
          QHBoxLayout* layout = new QHBoxLayout(w);
      
          for (int i = 0; i < 2; ++i)
          {
              HighlightFocusWidget* hightlightWidget = new HighlightFocusWidget(w);
              layout->addWidget(hightlightWidget);
              QHBoxLayout* innerLayout = new QHBoxLayout(hightlightWidget);
              innerLayout->setContentsMargins(8, 8, 8, 8);
      
              QWidget* paintOnScreenWidget = new PaintOnScreenWidget();
              innerLayout->addWidget(paintOnScreenWidget);
              QHBoxLayout* paintOnScreenLayout = new QHBoxLayout(paintOnScreenWidget);
              paintOnScreenLayout->setContentsMargins(0, 0, 0, 0);
      
              QWidget* overlayWidget = new QWidget(/*paintOnScreenWidget*/);
              overlayWidget->setAttribute(Qt::WA_DontShowOnScreen);
              paintOnScreenLayout->addWidget(overlayWidget);
          }
      
          w->setFixedSize(QSize(600, 400));
          w->show();
      
          return app.exec();
      }
      

      A context menu can be invoked for both of the widgets and this can lead to incorrect focus handling when the context menu is closed: Right-click in the left half of the window to invoke the menu for the left HightlightFocusWidget and then left-click on the other HighlightFocusWidget on the right. The widget on the right hand side will not get focused but will receive a mouse press event. It will however not receive the corresponding mouse release event afterwards (as the red frame is still being displayed).

      This behavior has to do with the Qt::WA_PaintOnScreen flag (+paintEngine() returning null) and the Qt::WA_DontShowOnScreen flag. I couldn't figure out exactly what is going wrong but found that QWidgetWindow::event() has special case handling for the Qt::WA_DontShowOnScreen flag, which when disabled correctly handles focus in the case described above.

      Oddly, a workaround for this is to directly set the parent of overlayWidget when it is constructed (commented out in the example above). This at least gets rid of the spurious mouse down event.

      Attachments

        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
            daniel.hofmann Daniel Hofmann
            Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

              Created:
              Updated:

              Gerrit Reviews

                There are no open Gerrit changes