Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QTabWidget with add button right after the tabs (like Firefox or Chrome)
Forum Updated to NodeBB v4.3 + New Features

QTabWidget with add button right after the tabs (like Firefox or Chrome)

Scheduled Pinned Locked Moved Solved General and Desktop
widgetqtabwidgetcustomize
5 Posts 3 Posters 2.5k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    Cryptkeeper
    wrote on 17 Jun 2022, 10:46 last edited by
    #1

    Hi,

    I want to realize a QTabWidget with an "add tab" button right after the last tab (just like the tab bar from Firefox or Chrome). I've tried different approaches but each with it's different disadvantages:

    1. Using a (disabled) tab with an add icon. This worked but if you have movable tabs, you could move other tabs beyond the add button.

    2. Using the setCornerWidget() method of QTabWidget. This works also but I would prefer to have the button right after the last tab instead of the widget corner. With a style sheet like "QTabWidget::right-corner { left: -100px; }" I managed to move the corner button towards the tab bar. However I don't know to get the right width and it seems pretty hacky to adapt the style sheet on geometry changes.

    3. So I came up with the idea of subclassing QTabWidget an modify the layout manually. But unfortunately the responsible function QTabWidget::setUpLayout() is non-virtual private. So no luck here...

    My ultimatively last idea would be to implement a QTabWidget like widget completely from scratch (possibly with copy & paste code from the original). But this seems to be almost overkill.

    Does anyone have a better idea?

    1 Reply Last reply
    0
    • S SGaist
      17 Jun 2022, 12:13

      Hi,

      Just thinking out loud, you could have a QPushButton that you manually position after the last tab. Note that I haven't tested the complexity of that variant (and if it would work well in your case).

      C Offline
      C Offline
      Cryptkeeper
      wrote on 17 Jul 2022, 21:52 last edited by
      #4

      @SGaist

      Unfortunately it's not that simple keeping the button independent from the QTabWidget. The edge cases in particular are somewhat more complex to deal with. For example if the widget is too narrow, the tab bar scroll buttons will appear and there will be no space left for the add button:

      tabbar.png

      So you have to consider the complete geometry of the QTabWidget which is defined in void QTabWidget::setUpLayout(bool onlyCheck):

      void QTabWidget::setUpLayout(bool onlyCheck)
      {
          /* ... */
      
          QStyleOptionTabWidgetFrame option;
          initStyleOption(&option);
          d->setLayoutItemMargins(QStyle::SE_TabWidgetLayoutItem, &option);
      
          QRect tabRect = style()->subElementRect(QStyle::SE_TabWidgetTabBar, &option, this);
          d->panelRect = style()->subElementRect(QStyle::SE_TabWidgetTabPane, &option, this);
          QRect contentsRect = style()->subElementRect(QStyle::SE_TabWidgetTabContents, &option, this);
          QRect leftCornerRect = style()->subElementRect(QStyle::SE_TabWidgetLeftCorner, &option, this);
          QRect rightCornerRect = style()->subElementRect(QStyle::SE_TabWidgetRightCorner, &option, this);
      
          d->tabs->setGeometry(tabRect);
          d->stack->setGeometry(contentsRect);
          if (d->leftCornerWidget)
              d->leftCornerWidget->setGeometry(leftCornerRect);
          if (d->rightCornerWidget)
              d->rightCornerWidget->setGeometry(rightCornerRect);
      
          /* ... */
      }
      

      As said, this method is unfortunately non-virtual and cannot be overridden. However, on closer inspection it turns out that in fact the current QStyle is defining the concrete layout. So after I took a deeper dive into Qt styling (and stepping through the various Qt abstraction layers ^^) I came up with the solution using in fact the right corner widget and a QProxyStyle implementation to modify the position:

      QRect FancyTabStyle::subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget) const
      {
          if ( subElement == QStyle::SE_TabWidgetRightCorner)
          {
              // adapted from void QTabWidget::setUpLayout(bool onlyCheck)
              QRect tabRect = QProxyStyle::subElementRect(QStyle::SE_TabWidgetTabBar, option, widget);
              QRect rightCornerRect = QProxyStyle::subElementRect(QStyle::SE_TabWidgetRightCorner, option, widget);
      
              // place the right corner widget right after the tab bar
              rightCornerRect.setRect( tabRect.left() + tabRect.width() + 4, 3, rightCornerRect.width(), rightCornerRect.height());
      
              return rightCornerRect;
          }
      
          return QProxyStyle::subElementRect(subElement, option, widget);
      }
      

      However, to get the correct button size and alignment (especially the vertical alignment) I had to do some additional tweakings (such as overriding the paintEvent and implementing a QAbstractButton subclass for the add button). I implemented the solution with my FancyTabWidget which additionally provides editable tab names and published it on github: https://github.com/SM-nzberg/QtFancyTabWidget

      1 Reply Last reply
      1
      • S Offline
        S Offline
        SGaist
        Lifetime Qt Champion
        wrote on 17 Jun 2022, 12:13 last edited by
        #2

        Hi,

        Just thinking out loud, you could have a QPushButton that you manually position after the last tab. Note that I haven't tested the complexity of that variant (and if it would work well in your case).

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        J C 2 Replies Last reply 17 Jun 2022, 14:12
        0
        • S SGaist
          17 Jun 2022, 12:13

          Hi,

          Just thinking out loud, you could have a QPushButton that you manually position after the last tab. Note that I haven't tested the complexity of that variant (and if it would work well in your case).

          J Offline
          J Offline
          JoeCFD
          wrote on 17 Jun 2022, 14:12 last edited by
          #3

          @SGaist Basically add and move an overlay button(separate from qtabwidget) at the right spot on top of the qtabwidget.

          1 Reply Last reply
          0
          • S SGaist
            17 Jun 2022, 12:13

            Hi,

            Just thinking out loud, you could have a QPushButton that you manually position after the last tab. Note that I haven't tested the complexity of that variant (and if it would work well in your case).

            C Offline
            C Offline
            Cryptkeeper
            wrote on 17 Jul 2022, 21:52 last edited by
            #4

            @SGaist

            Unfortunately it's not that simple keeping the button independent from the QTabWidget. The edge cases in particular are somewhat more complex to deal with. For example if the widget is too narrow, the tab bar scroll buttons will appear and there will be no space left for the add button:

            tabbar.png

            So you have to consider the complete geometry of the QTabWidget which is defined in void QTabWidget::setUpLayout(bool onlyCheck):

            void QTabWidget::setUpLayout(bool onlyCheck)
            {
                /* ... */
            
                QStyleOptionTabWidgetFrame option;
                initStyleOption(&option);
                d->setLayoutItemMargins(QStyle::SE_TabWidgetLayoutItem, &option);
            
                QRect tabRect = style()->subElementRect(QStyle::SE_TabWidgetTabBar, &option, this);
                d->panelRect = style()->subElementRect(QStyle::SE_TabWidgetTabPane, &option, this);
                QRect contentsRect = style()->subElementRect(QStyle::SE_TabWidgetTabContents, &option, this);
                QRect leftCornerRect = style()->subElementRect(QStyle::SE_TabWidgetLeftCorner, &option, this);
                QRect rightCornerRect = style()->subElementRect(QStyle::SE_TabWidgetRightCorner, &option, this);
            
                d->tabs->setGeometry(tabRect);
                d->stack->setGeometry(contentsRect);
                if (d->leftCornerWidget)
                    d->leftCornerWidget->setGeometry(leftCornerRect);
                if (d->rightCornerWidget)
                    d->rightCornerWidget->setGeometry(rightCornerRect);
            
                /* ... */
            }
            

            As said, this method is unfortunately non-virtual and cannot be overridden. However, on closer inspection it turns out that in fact the current QStyle is defining the concrete layout. So after I took a deeper dive into Qt styling (and stepping through the various Qt abstraction layers ^^) I came up with the solution using in fact the right corner widget and a QProxyStyle implementation to modify the position:

            QRect FancyTabStyle::subElementRect(SubElement subElement, const QStyleOption *option, const QWidget *widget) const
            {
                if ( subElement == QStyle::SE_TabWidgetRightCorner)
                {
                    // adapted from void QTabWidget::setUpLayout(bool onlyCheck)
                    QRect tabRect = QProxyStyle::subElementRect(QStyle::SE_TabWidgetTabBar, option, widget);
                    QRect rightCornerRect = QProxyStyle::subElementRect(QStyle::SE_TabWidgetRightCorner, option, widget);
            
                    // place the right corner widget right after the tab bar
                    rightCornerRect.setRect( tabRect.left() + tabRect.width() + 4, 3, rightCornerRect.width(), rightCornerRect.height());
            
                    return rightCornerRect;
                }
            
                return QProxyStyle::subElementRect(subElement, option, widget);
            }
            

            However, to get the correct button size and alignment (especially the vertical alignment) I had to do some additional tweakings (such as overriding the paintEvent and implementing a QAbstractButton subclass for the add button). I implemented the solution with my FancyTabWidget which additionally provides editable tab names and published it on github: https://github.com/SM-nzberg/QtFancyTabWidget

            1 Reply Last reply
            1
            • S Offline
              S Offline
              SGaist
              Lifetime Qt Champion
              wrote on 19 Jul 2022, 19:52 last edited by
              #5

              Thanks for the detailed deep dive !

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved