Solved Set vertical tab close button issue
-
Hello!
I want to change the default tab widget close button and set my icon instead. The problem is that it draws the icon on the text.
Code:
void AppTabBar::paintEvent(QPaintEvent *event) { QStylePainter painter(this); QStyleOptionTab opt; for (int i = 0; i < this->count(); i++) { initStyleOption(&opt, i); opt.text = painter.fontMetrics().elidedText(opt.text, Qt::ElideRight, 70); painter.drawControl(QStyle::CE_TabBarTabShape, opt); painter.save(); QSize s = opt.rect.size(); if (tabPos != AppTabPosition::Top && tabPos != AppTabPosition::Bottom) { s.transpose(); } QRect r(QPoint(), s); r.moveCenter(opt.rect.center()); opt.rect = r; QPoint c = tabRect(i).center(); painter.translate(c); if (tabPos == AppTabPosition::Left) { painter.rotate(90); } else if (tabPos == AppTabPosition::Right) { painter.rotate(270); //90 - left pos, 270 - right pos } painter.translate(-c); painter.drawControl(QStyle::CE_TabBarTabLabel, opt); painter.restore(); } QWidget::paintEvent(event); } void AppTabStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == PE_IndicatorTabClose) { int size = proxy()->pixelMetric(QStyle::PM_SmallIconSize); QIcon::Mode mode = option->state & State_Enabled ? (option->state & State_Raised ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; if (!(option->state & State_Raised) && !(option->state & State_Sunken) && !(option->state & QStyle::State_Selected)) { mode = QIcon::Disabled; } QIcon::State state = option->state & State_Sunken ? QIcon::On : QIcon::Off; QPixmap pixmap = QIcon(":/Icons/cross_icon.png").pixmap(size, mode, state); proxy()->drawItemPixmap(painter, option->rect, Qt::AlignRight, pixmap); } else { QProxyStyle::drawPrimitive(element, option, painter, widget); } }
Screenshot:
Any ideas how to draw it to the right? Thank you.
-
Hello!
Finally! I have fixed the issue with vertical tab close button position.
Code:
if (tabPos != AppTabPosition::Top && tabPos != AppTabPosition::Bottom) { s.transpose(); if (this->tabsClosable()) { // check if tab is closable QRect optRect = opt.rect; optRect.setX(90); // set X pos of close button optRect.setY(optRect.y() + 8); // calcs the Y pos of close button optRect.setSize(QSize(12, 12)); this->tabButton(i, QTabBar::RightSide)->setGeometry(optRect); } }
Here I do not change the default
opt.rect
but instead I copy it to newQRect
. Then I change the size and position ofoptRect
and finally set geometry totabButtton
.Screenshot:
The issue is resolved.
-
I think I know what the problem is. I will reply soon.
-
Nope, my solution does not work. It draws 2 close buttons, then I hide the first one and display mine. Now, the problem is when clicking on X it does nothing.
if (tabPos != AppTabPosition::Top && tabPos != AppTabPosition::Bottom) { s.transpose(); if (this->tabsClosable()) { // check if tab is closable opt.rect.setX(90); // set X pos of close button this->tabButton(i, QTabBar::RightSide)->hide(); // hide the default close button painter.drawPrimitive(QStyle::PE_IndicatorTabClose, opt); //draw new close button opt.rect.setX(0); // set X pos to default } }
How to connect the signal to a primitive type? Or is there any better solution to display the custom close tab icon. Thank you.
-
I think, the easiest way is to change the
QRect
in thedrawPrimitive
method but when I set for exampleX
coordinate to90
, then my close icon becomes invisible.Code:
void AppTabBarStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { if (element == PE_IndicatorTabClose) { int size = proxy()->pixelMetric(QStyle::PM_SmallIconSize); QIcon::Mode mode = option->state & State_Enabled ? (option->state & State_Raised ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; if (!(option->state & State_Raised) && !(option->state & State_Sunken) && !(option->state & QStyle::State_Selected)) { mode = QIcon::Disabled; } QIcon::State state = option->state & State_Sunken ? QIcon::On : QIcon::Off; QPixmap pixmap = QIcon(":/Icon/red_cross_icon.png").pixmap(size, mode, state); qDebug() << "Size: " << option->rect.size() << " | x:" << option->rect.x() << " | y:" << option->rect.y(); QRect testRect; testRect.setX(90); testRect.setY(5); testRect.setSize(option->rect.size()); proxy()->drawItemPixmap(painter, testRect, Qt::AlignRight, pixmap); } else { QProxyStyle::drawPrimitive(element, option, painter, widget); } }
Any ideas how to override the default tab bar button? Thank you.
-
I have set
setTabsClosable(false);
, then created theTabBarLabel
class and set it tosetTabButton
method. It closes the tabs but the issue with overlapping the tab text still exists:Screenshot:
It is a lot of code, so I have created and uploaded to
Mega
the test example to illustrate this issue. Here is my test example: TabExample.zip (6kb)Any ideas how to change position of tab bar close button? Thank you.
-
Hi,
@Cobra91151 said in Set vertical tab close button issue:testRect.setX(90);
testRect.setY(5);
testRect.setSize(option->rect.size());What are the rect values ?
I would say that you are currently drawing in the wrong coordinate system.To get started, I would either get the code from the base model or one of the style examples in order to go from a working state.
-
Hello!
These are just test values. I no longer use
void AppTabBarStyle::drawPrimitive
to draw the X icon. Now, I use theTabBarLabel
which inherits fromQLabel
. I will post the code forTabBarLabel
soon. -
So, here is my code below:
tabbarlable.h
#ifndef TABBARLABEL_H #define TABBARLABEL_H #include <QLabel> #include <QObject> #include <QPaintEvent> #include <QMouseEvent> #include "apptabbar.h" #include <QDebug> class TabBarLabel : public QLabel { Q_OBJECT public: TabBarLabel(AppTabBar *parent, QPixmap image); ~TabBarLabel(); private: int tabIndex() const; protected: void mousePressEvent(QMouseEvent *event); private: AppTabBar *tabBar; }; #endif // TABBARLABEL_H
tabbarlabel.cpp
#include "tabbarlabel.h" TabBarLabel::TabBarLabel(AppTabBar *parent, QPixmap image) : QLabel(parent), tabBar(parent) { this->setPixmap(image); this->setScaledContents(true); this->setStyleSheet("background: transparent;"); this->setToolTip(QObject::tr("Close")); } void TabBarLabel::mousePressEvent(QMouseEvent *event) { qDebug() << "Clicked..."; QMetaObject::invokeMethod(tabBar, "tabCloseRequested", Qt::DirectConnection, Q_ARG(int, this->tabIndex())); QLabel::mousePressEvent(event); } int TabBarLabel::tabIndex() const { for (int i = 0; i < tabBar->count(); i++) { if (tabBar->tabButton(i, QTabBar::RightSide) == this) { return i; } } return -1; } TabBarLabel::~TabBarLabel() { }
testwidget.cpp
TestWidget::TestWidget(QWidget *parent) : QWidget(parent) { setMinimumSize(900, 560); AppTabControl *tabControl = new AppTabControl(this, 30, 115); tabControl->setTabsClosable(false); connect(tabControl->tabBar(), &AppTabBar::tabCloseRequested, this, [this, tabControl](int tabIndex) { tabControl->removeTab(tabIndex); }); this->setStyleSheet("QTabBar::tab {color: #000000; font-weight: bold; font-size: 10px; font-family: Gotham, Helvetica Neue, Helvetica, Arial, sans-serif;} " "QTabBar::tab:selected {background-color: #FA9944; color: #000000; border-top: 1px solid #FA9944;} " "QTabBar::tab:hover {color: #000000; border-top: 1px solid #FA9944; background-color: #FFFFFF;}"); AppTabBar *tabBar1 = new AppTabBar(tabControl); TabBarLabel *closeTab2Label = new TabBarLabel(reinterpret_cast<AppTabBar*>(tabControl->tabBar()), QIcon(":/Icons/close_tab_icon.png").pixmap(12, 12)); TabBarLabel *closeTab3Label = new TabBarLabel(reinterpret_cast<AppTabBar*>(tabControl->tabBar()), QIcon(":/Icons/close_tab_icon.png").pixmap(12, 12)); AppTabBar *tabBar2 = new AppTabBar(tabControl); AppTabBar *tabBar3 = new AppTabBar(tabControl); tabControl->addTab(tabBar1, "Tab1"); int tab2Index = tabControl->addTab(tabBar2, "Tab2"); int tab3Index = tabControl->addTab(tabBar3, "Tab3"); tabControl->tabBar()->setTabButton(tab2Index, QTabBar::RightSide, closeTab2Label); tabControl->tabBar()->setTabButton(tab3Index, QTabBar::RightSide, closeTab3Label); QHBoxLayout *mainLayout = new QHBoxLayout(); mainLayout->setContentsMargins(QMargins()); mainLayout->addWidget(tabControl); setLayout(mainLayout); }
The actual vertical tab bar draws here:
void AppTabBar::paintEvent(QPaintEvent *event) { QStylePainter painter(this); QStyleOptionTab opt; for (int i = 0; i < this->count(); i++) { initStyleOption(&opt, i); painter.drawControl(QStyle::CE_TabBarTabShape, opt); painter.save(); QSize s = opt.rect.size(); s.transpose(); QRect r(QPoint(), s); r.moveCenter(opt.rect.center()); opt.rect = r; QPoint c = tabRect(i).center(); painter.translate(c); painter.rotate(90); painter.translate(-c); painter.drawControl(QStyle::CE_TabBarTabLabel, opt); painter.restore(); } QWidget::paintEvent(event); }
I have checked the
X
andY
forTabBarLabel
:
qDebug() << "x: " << tabControl->tabBar()->tabButton(tab2Index, QTabBar::RightSide)->pos().x() << " | y:" << tabControl->tabBar()->tabButton(tab2Index, QTabBar::RightSide)->pos().y();
it returnsx: 52 | y: 39
This issue only happens for tab position -
QTabWidget::West
andQTabWidget::East
. Any ideas how to fix it? Thank you. -
I think the following:
s.transpose();
swaps the width and height around in order to display the vertical tabs correctly. But the tab bar close button does not know about it and wrongly calculates the position. I need to reimplement the method where tab bar close button makes the calculation/drawing on a tab to fix this issue. Now, the question is what is the name of such method. I think it could be one of this two methods:1. AppTabBar::paintEvent 2. TabBarLabel::paintEvent
-
Hello!
Finally! I have fixed the issue with vertical tab close button position.
Code:
if (tabPos != AppTabPosition::Top && tabPos != AppTabPosition::Bottom) { s.transpose(); if (this->tabsClosable()) { // check if tab is closable QRect optRect = opt.rect; optRect.setX(90); // set X pos of close button optRect.setY(optRect.y() + 8); // calcs the Y pos of close button optRect.setSize(QSize(12, 12)); this->tabButton(i, QTabBar::RightSide)->setGeometry(optRect); } }
Here I do not change the default
opt.rect
but instead I copy it to newQRect
. Then I change the size and position ofoptRect
and finally set geometry totabButtton
.Screenshot:
The issue is resolved.