Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Why isn't a QComboBox positioned correctly in a layout?



  • A QComboBox seems to be shifted under the neighbouring widget in a layout. The combo box is shifted 1px down and 2px left.

    Here's the code to reproduce the problem:

    #include <QtGui/qpainter.h>
    #include <QtWidgets/qcombobox.h>
    #include <QtWidgets/qboxlayout.h>
    #include <QtWidgets/qmainwindow.h>
    #include <QtWidgets/qapplication.h>
    
    template <typename Base>
    class Rectangle : public Base {
    public:
      explicit Rectangle(QColor color)
        : color{color} {
        Base::setFixedSize(100, 20);
      }
    
    private:
      QColor color;
      
      void paintEvent(QPaintEvent *) override {
        QPainter{this}.fillRect(Base::rect(), color);
      }
    };
    
    int main(int argc, char **argv) {
      QApplication app{argc, argv};
      QMainWindow window;
      QWidget central;
      window.setCentralWidget(&central);
      QHBoxLayout layout{&central};
      layout.setSpacing(0);
      layout.setContentsMargins(0, 0, 0, 0);
      layout.addWidget(new Rectangle<QWidget>{Qt::red});
      layout.addWidget(new Rectangle<QComboBox>{Qt::blue});
      window.show();
      return app.exec();
    }
    

    This is what I see when the blue widget is a QComboBox:

    0_1563695949682_combobox.png

    This is what I see when the blue widget is a plain old QWidget:

    0_1563695961454_widget.png

    I expect both of those screenshots to be the same (but they aren't).

    I'm using macOS 10.14.5 and Qt 5.12.3.



  • I updated to Qt 5.13.0 and the issue persists. Could this be a bug?



  • I'm not even sure how to work around this bug because I can't move or set the margins of the combo box while it's in a layout. Do I have to manually position the widget relative to the parent and completely remove the layout?


  • Moderators

    @Kerndog73 What do you get if you call this in Base::paintEvent()? qDebug() << rect();



  • @JKSH Both the QComboBox and the QWidget are QRect(0,0 100x20)


  • Moderators

    @Kerndog73 said in Why isn't a QComboBox positioned correctly in a layout?:

    @JKSH Both the QComboBox and the QWidget are QRect(0,0 100x20)

    Ok, their sizes are as expected.

    What about qDebug() << geometry(); ?



  • @JKSH The QWidget is QRect(1,39 100x20) and the QComboBox is QRect(99,40 100x20). I think the combo box geometry should be QRect(101,39 100x20).



  • I updated to macOS 10.14.6 and the issue persists. I also found a thread about a similar issue. I might be able to use a similar solution (make the widget a little bigger and offset the painting). I'm thinking about creating a bug report but my previous bug reports have gotten no response after months so I don't think I'll bother.


  • Moderators

    @Kerndog73 said in Why isn't a QComboBox positioned correctly in a layout?:

    The QWidget is QRect(1,39 100x20) and the QComboBox is QRect(99,40 100x20). I think the combo box geometry should be QRect(101,39 100x20).

    ...

    I'm thinking about creating a bug report but my previous bug reports have gotten no response after months so I don't think I'll bother.

    You might have better luck asking in the Interest mailing list (you must subscribe first). Qt engineers are active on that list, and might be able to explain why the offset occurs.

    I might be able to use a similar solution (make the widget a little bigger and offset the painting).

    That sounds like a pracctical workaround



  • @JKSH said in Why isn't a QComboBox positioned correctly in a layout?:

    You might have better luck asking in the Interest mailing list (you must subscribe first).

    I'll give it a try!



  • I think the problem is because of this piece of code from qtbase/src/plugins/styles/mac/qmacstyle_mac.mm file. Somehow they call setLayoutItemMargins(...) function under the SE_ComboBoxLayoutItem case statement and it breaks QComboBox's alignment in a layout. I had this problem today and I fixed it after I removed the case statement in my own custom subclass of QMacStyle.

    In order to fix the problem, you have 3 options:

    1. Set Qt::WA_LayoutUsesWidgetRect attribute true on the comboBox:
    comboBox->setAttribute(Qt::WA_LayoutUsesWidgetRect);
    
    1. Set your application style to something else, like fusion style:
    QApplication::setStyle("fusion");
    
    1. Implement your own custom style class based on QMacStyle via QProxyStyle (which is the way I do), then reimplement subElementRect(...) function and make sure the function returns an invalid/default constructed QRect() for SE_ComboBoxLayoutItem case. Finally set your application style to your own custom style class.

    2. BONUS (pretty nasty option): As a bonus option, you can follow the "hacky" way below:
      4.1. Add QT += widgets-private to your .pro file
      4.2. Insert the comboBox to the layout
      4.3. Call setLayoutItemMargins(...) function of the comboBox

      #include <private/qwidget_p.h>
      class Hacker : public QWidget { public: Q_DECLARE_PRIVATE(QWidget) };
      reinterpret_cast<Hacker*>(comboBox)->d_func()->setLayoutItemMargins(0, 0, 0, 0); // Might be an undefined behavior based on C++ standards
      

Log in to reply