I have never found a nice way to do that. But, I used the fact that you can store raw data in the QTabBar
to pin the tabs and undo a move if it was a pinned tab. It's not perfect and I still have some ugly behavior, but I didn't want to use mouse events, neither.
First, create a struct to store the current state of a tab:
struct PinnedTab
{
bool isPinned;
int currentIndex;
};
Q_DECLARE_METATYPE(PinnedTab); // For QVariant
Then, create a custom QTabBar
to handle the move and use QTabWidget
to replace the current tab bar (you have to do that before inserting the tabs):
class Bar: public QTabBar
{
public:
void pin(int const index)
{
PinnedTab info;
info.isPinned = true;
info.currentIndex = index; // TODO: move the tab to the left and do not use current index
setTabData(index, QVariant::fromValue(info));
}
Bar(QWidget* parent=nullptr): QTabBar(parent)
{}
virtual void tabLayoutChange() override
{
for (int i = 0; i != count(); ++i) // Check if a pinned tab has moved
{
if (tabData(i).isValid())
{
PinnedTab const info = tabData(i).value<PinnedTab>();
if (info.isPinned == true && i != info.currentIndex) {
rollbackLayout();
return;
}
}
}
for (int i = 0; i != count(); ++i)
{
if (tabData(i).isValid())
{
PinnedTab info = tabData(i).value<PinnedTab>();
info.currentIndex = i;
setTabData(i, QVariant::fromValue(info));
}
else
{
PinnedTab info;
info.isPinned = false;
info.currentIndex = i;
setTabData(i, QVariant::fromValue(info));
}
}
}
void rollbackLayout() {
for (int i = 0; i != count(); ++i)
{
if (tabData(i).isValid())
{
PinnedTab const info = tabData(i).value<PinnedTab>();
if (i != info.currentIndex) {
moveTab(i, info.currentIndex);
}
}
}
}
};
tabLayoutChange
is called when the layout has changed. So, it will be called when you move a tab.
the rollbackLayout
method is used to move each tab to the last position stored in the tab data.
Call pin
to pin a tab with the given index.
I simplified my code for more clarity and you may have to redefine some behavior (for now, if you pin a tab, it will keep its current position and it will not handle the insert/remove tabs).