FiveEight
FiveEight

Reputation: 73

How to use an event handler to detect clicking on the empty space after tab bar?

I have a QTabWidget, and I need to trigger an event when i press the area on the tab widget where you normally would see the tab name, accept its an empty space with no tab.

Clicking the red area should trigger an event:

i want to click the red area to trigger an event

Here is how the result should look like:

here is a gif of what i am talking about

Upvotes: 3

Views: 308

Answers (2)

Atmo
Atmo

Reputation: 3967

What you ask is usually done by a button next to the last tab, same as what modern browsers (chrome, edge, ...) do to ease the creation of new tabs.

We can copy that principle to do what you want, the only difference being that the button will extend far to the right of the widget. IMHO, that is much more convenient and easier to customize than installing an event filter.
In the code below, I added some bits for you to uncomment if you prefer having a smaller button.

#include <QtWidgets/QApplication>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTabBar>
#include <QtWidgets/QTabWidget>


int main(int argc, char** argv)
{
    QApplication a(argc, argv);
    a.setQuitOnLastWindowClosed(true);

    QTabWidget w;
    w.addTab(new QWidget(), "Tab 1");

    QPushButton* tabButton = new QPushButton(&w);
    tabButton->setFlat(true);
    tabButton->stackUnder(w.tabBar());

    auto moveTabAddButton = [&w, tabButton]()
    {
        tabButton->move(w.tabBar()->width(), 0);
    };

    QObject::connect(tabButton, &QAbstractButton::clicked, &w, [&w, &moveTabAddButton]()
        {
            w.addTab(new QWidget(), QStringLiteral("Tab %1").arg(1 + w.count()));
            moveTabAddButton();
        }
    );
    QObject::connect(&w, &QTabWidget::tabCloseRequested, tabButton, moveTabAddButton);

//We make the button so wide it will always cover the space left free by tabs.
    tabButton->setFixedSize(100000, w.tabBar()->tabRect(0).height());
//Uncomment the entire next block to turn the button into
//a + button next to the last tab (+ optionally remove the previous line).
//tabButton->setText("+");
//tabButton->setFixedSize(w.tabBar()->tabRect(0).height(),
//                        w.tabBar()->tabRect(0).height());

//Uncomment the next block of rows to make the button round.
    //tabButton->setStyleSheet(QStringLiteral(
    //    "QPushButton{" \
    //    "border-radius: %1px;" \
    //    "}" \
    //    "QPushButton:hover{" \
    //    "background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1," \
    //    "                               stop: 0 #f6f7fa, stop: 1 #dadbde); " \
    //    "}" \
    //    "QPushButton:pressed{" \
    //    "background-color: qlineargradient(x1 : 0, y1 : 0, x2 : 0, y2 : 1," \
    //    "                                  stop : 0 #dadbde, stop: 1 #f6f7fa);" \
    //    "}" \
    //).arg(tabButton->height() / 2));

    w.show();
    moveTabAddButton();

    return a.exec();
}

I let you customize the little bits such as the stylesheets, the margin between the last tab and the button, the icon (instead of text) on the button if you want...

Upvotes: 2

user17726418
user17726418

Reputation: 2363

You could install an event filter on your QTabWidget, and use QMouseEvent position to detect if it's on the empty space after the tab bar, and add tabs there.

There are many ways you can detect that, here's one of them:

#include <QObject>
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>
#include <QTabWidget>
#include <QTabBar>

class myEventFilter : public QObject
{
    Q_OBJECT

public:
    myEventFilter (QObject *parent = nullptr) {}

protected:
    bool eventFilter(QObject * obj, QEvent * event) override
    {
        //check if event is a mouse button press
        if(event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            //obj is the object that has the event filter installed on
            //here it is a QTabWidget, you can get it like this
            QTabWidget *tabWidget = static_cast<QTabWidget *>(obj);

            //here is where you can have multiple ways to check mouse event position
            //you can change it however it suits you
            //but be sure it works
            if(mouseEvent->position().toPoint().x() > tabWidget->tabBar()->width() &&
               mouseEvent->position().toPoint().y() < tabWidget->tabBar()->height())
            {
                //add tabs here
                tabWidget->addTab(new QWidget(),"tab");
            }
        }

        return QObject::eventFilter(obj, event);
    }
};

Here's how it looks, I'm clicking on different areas to test that it only adds a tab when clicked on the space after the tab bar:

enter image description here

Notes:

  • If you install the event filter on the tab widget, tab bar will not get mouse press events.
  • You can use QCursor::pos() instead of QMouseEvent, but you'll need to map positions.
  • On Waterfox, a new tab is added on mouse double click, which would be more convenient in case of a misclick.

Upvotes: 2

Related Questions