Jawad Hussain Shah
Jawad Hussain Shah

Reputation: 141

How to make tabs like chrome browser

I have created a browser but when tab reached to the last of the UI the tab not collapse like chrome but still remain in same width.

What I want?

I want when tab reached to last the tab should be collapse like chrome browser.

What I want. In the above pic the width of the tabs remain same not collapse or shrink... but in the below pic the tabs in the chrome browser shrink gradually as we add more tabs

enter image description here

Upvotes: 0

Views: 1092

Answers (1)

musicamante
musicamante

Reputation: 48509

QTabBar doesn't support that, and you need to implement it on your own.

The solution uses tabSizeHint(), which returns a size that normally suits the contents of each tab, but in this case you need to return a default standard size and a smaller one whenever the available space is less than required.

Note that since resizing the tabs calls again both tabSizeHint() and minimumTabSizeHint(), you need to add a system that avoids infinite recursion.

The current QStyle and font metrics are used to compute the minimum and default sizes, and the tab widget needs to update the optimal size of the tab bar whenever it's resized.

class ShrinkTabBar(QtWidgets.QTabBar):
    _widthHint = -1
    _initialized = False
    _recursiveCheck = False
    addClicked = QtCore.pyqtSignal()
    def __init__(self, parent):
        super().__init__(parent)
        self.setElideMode(QtCore.Qt.ElideRight)
        self.setExpanding(False)
        self.setTabsClosable(True)
        self.addButton = QtWidgets.QToolButton(self.parent(), text='+')
        self.addButton.clicked.connect(self.addClicked)
        self._recursiveTimer = QtCore.QTimer(singleShot=True, timeout=self._unsetRecursiveCheck, interval=0)
        self._closeIconTimer = QtCore.QTimer(singleShot=True, timeout=self._updateClosable, interval=0)

    def _unsetRecursiveCheck(self):
        self._recursiveCheck = False

    def _updateClosable(self):
        self.setTabsClosable(self._widthHint >= self._minimumCloseWidth)

    def _computeHints(self):
        if not self.count() or self._recursiveCheck:
            return
        self._recursiveCheck = True

        opt = QtWidgets.QStyleOptionTab()
        self.initStyleOption(opt, 0)
        width = self.style().pixelMetric(QtWidgets.QStyle.PM_TabBarTabHSpace, opt, self)
        iconWidth = self.iconSize().width() + 4
        self._minimumWidth = width + iconWidth

        # default text widths are arbitrary
        fm = self.fontMetrics()
        self._minimumCloseWidth = self._minimumWidth + fm.width('x' * 4) + iconWidth
        self._defaultWidth = width + fm.width('x' * 17)
        self._defaultHeight = super().tabSizeHint(0).height()
        self._minimumHint = QtCore.QSize(self._minimumWidth, self._defaultHeight)
        self._defaultHint = self._tabHint = QtCore.QSize(self._defaultWidth, self._defaultHeight)

        self._initialized = True
        self._recursiveTimer.start()

    def _updateSize(self):
        if not self.count():
            return
        frameWidth = self.style().pixelMetric(
            QtWidgets.QStyle.PM_DefaultFrameWidth, None, self.parent())
        buttonWidth = self.addButton.sizeHint().width()
        self._widthHint = (self.parent().width() - frameWidth - buttonWidth) // self.count()
        self._tabHint = QtCore.QSize(min(self._widthHint, self._defaultWidth), self._defaultHeight)
        # dirty trick to ensure that the layout is updated
        if not self._recursiveCheck:
            self._recursiveCheck = True
            self.setIconSize(self.iconSize())
            self._recursiveTimer.start()

    def minimumTabSizeHint(self, index):
        if not self._initialized:
            self._computeHints()
        return self._minimumHint

    def tabSizeHint(self, index):
        if not self._initialized:
            self._computeHints()
        return self._tabHint

    def tabLayoutChange(self):
        if self.count() and not self._recursiveCheck:
            self._updateSize()
            self._closeIconTimer.start()

    def tabRemoved(self, index):
        if not self.count():
            self.addButton.setGeometry(1, 2, 
                self.addButton.sizeHint().width(), self.height() - 4)

    def changeEvent(self, event):
        super().changeEvent(event)
        if event.type() in (event.StyleChange, event.FontChange):
            self._updateSize()

    def resizeEvent(self, event):
        if not self.count():
            super().resizeEvent(event)
            return
        self._recursiveCheck = True
        super().resizeEvent(event)
        height = self.sizeHint().height()
        if height < 0:
            # a tab bar without tabs returns an invalid size
            height = self.addButton.height()
        self.addButton.setGeometry(self.geometry().right() + 1, 2, 
            self.addButton.sizeHint().width(), height - 4)
        self._closeIconTimer.start()
        self._recursiveTimer.start()


class ShrinkTabWidget(QtWidgets.QTabWidget):
    addClicked = QtCore.pyqtSignal()
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._tabBar = ShrinkTabBar(self)
        self.setTabBar(self._tabBar)
        self._tabBar.addClicked.connect(self.addClicked)

    def resizeEvent(self, event):
        self._tabBar._updateSize()
        super().resizeEvent(event)

Upvotes: 1

Related Questions