PDA

View Full Version : Custom QTabBar issue when movable



Alundra
4th January 2018, 13:19
Hi,
I have a custom QMainWindow with a QTabBar as center widget, this is basically a tabbed window to have multiple QMainWindow inside.
All works correctly but I have to disable the move on tab because if I enable it it gives weird behavior when I remove one tab during the move.
Basically I stay clicked on one tab and if the cursor is outside the QTabBar then the tab is removed and the QMainWindow is then floating.
The issue is the mouse button is not released so sometimes another tab continue to move, nothing should move anymore.
I tried all way to disable the move when I remove one tab but no way found.. surely it absolutely wait mouse release event..
The only way I think of is to write a custom move of tab and manage all myself.
Is it possible to do it ? Or one hack possible ?
Maybe a miss in QTabBar code, needs to check if the current pressed is the removed tab, maybe not safe on this case ?

Another thing is I would know if it's possible to enable the move of the tab.
example : I remove tab, the window is floating, I tab again
On this situation the tab should move, the mouse button is not released.
Only way is to simulate a click event ?

Thanks

d_stranz
4th January 2018, 17:13
I'm sorry, my magic crystal ball that allows me to see invisible code is not working today. If it was working, I might be able to look at your code and give you an idea about what was wrong.

Maybe someone else has a better crystal ball and can help you out.

Alundra
18th January 2018, 13:57
Here the code of the tab bar which inherit QTabBar.
It handle the move of the full window when you click not on a tab and handle when a tab is moved outside the tab bar.
When a tab is moved outside a tab bar the tab is removed and the QMainWindow widget is now floating.
Basically the same behavior like web browser for tabs.

The two issues without any luck finding a solution yet :
1) When the tab is removed if the tab widget is set to movable, remove tab is called but another tab is then moving during the QMainWindow is moving.
The moving should be disabled for all tabs and the animation of the tab bar should just be played to set the tab correctly placed.
Maybe a bug of QTabWidget which doesn't handle the remove tab correctly when it's moving, he waits a release mouse button.

2) When a tab is put in another or the same tab widget during the move when the QMainWindow is floating, the tab is not moving.
The user has to release the mouse button and click on the tab, the tab should be moving because the mouse button is not released.

Thank you for the help


class CTabbedWindowTabWidget : public QTabWidget
{
public:

CTabbedWindowTabWidget( QWidget* Parent = nullptr ) :
QTabWidget( Parent )
{
setTabBar( new CTabbedWindowTabBar( this ) );
}
};



CTabbedWindowTabBar::CTabbedWindowTabBar( QWidget* Parent ) :
QTabBar( Parent ),
m_DragWindow( nullptr ),
m_IsDraggingTab( false ),
m_IsDraggingWindow( false )
{
}

void CTabbedWindowTabBar::mouseMoveEvent( QMouseEvent* event )
{
// Base class.
QTabBar::mouseMoveEvent( event );

// Check the dragging window state.
if( m_IsDraggingWindow )
{
// Get the window.
CTabbedWindow* TabbedWindow = static_cast< CTabbedWindow* >( parent()->parent()->parent() );

// Move the window is only possible if the window is not maximized.
if( TabbedWindow->isMaximized() )
TabbedWindow->showNormal();

// Move the window using this delta.
TabbedWindow->move( QCursor::pos() - m_DragWindowOffsetPos );

// Stop here.
return;
}

// Check the dragging tab state.
if( m_IsDraggingTab == false )
return;

// Check if the drag window is not valid.
if( m_DragWindow == nullptr )
{
// Test if the mouse is outside the tab bar.
if( rect().contains( event->pos() ) == false )
{
// Get the tab widget.
QTabWidget* TabWidget = static_cast< QTabWidget* >( parent() );

// Don't create a new window if the tab bar only has 1 tab.
if( TabWidget->count() == 1 )
{
m_DragWindow = static_cast< CTabbedWindow* >( TabWidget->parent()->parent() );
}
else
{
// Get the main window.
QMainWindow* MainWindow = static_cast< QMainWindow* >( TabWidget->currentWidget() );

// Remove the tab.
static_cast< CTabbedWindow* >( TabWidget->parent()->parent() )->RemoveTab( TabWidget->currentIndex() );

// Add the tab on the new window.
m_DragWindow = new CTabbedWindow;
m_DragWindow->AddTab( MainWindow );
}

// Set the window properties.
m_DragWindow->setWindowOpacity( 0.5 );
m_DragWindow->setAttribute( Qt::WA_TransparentForMouseEvents );

// Show and move the window.
m_DragWindow->show();
m_DragWindow->move( event->globalPos() );
}
}
else
{
// Find the tab bar under the mouse.
CTabbedWindowTabBar* TargetTabBar = qobject_cast< CTabbedWindowTabBar* >( QApplication::widgetAt( event->globalPos() ) );

// Check if a tab bar is found.
if( TargetTabBar )
{
CTabbedWindow* TargetTabbedWindow = static_cast< CTabbedWindow* >( TargetTabBar->parent()->parent()->parent() );
QMainWindow* MainWindow = m_DragWindow->GetCurrentTabWindow();
m_DragWindow->RemoveTab( m_DragWindow->GetCurrentTab() );
TargetTabbedWindow->AddTab( MainWindow );
m_IsDraggingTab = false;
m_DragWindow = nullptr;
}
else
{
m_DragWindow->move( event->globalPos() );
}
}
}

void CTabbedWindowTabBar::mousePressEvent( QMouseEvent* event )
{
// Base class.
QTabBar::mousePressEvent( event );

// Actions only possible with the left button.
if( event->button() == Qt::LeftButton )
{
// Get the tab index, -1 is returned if no tab found.
const int TabIndex = tabAt( event->pos() );

// Check the widget on each side of the tab.
QWidget* LeftSide = tabButton( TabIndex, QTabBar::LeftSide );
QWidget* RightSide = tabButton( TabIndex, QTabBar::RightSide );

// The dragging tab flag is enabled only if one tab is found and if one widget is in the tab.
m_IsDraggingTab = ( TabIndex != -1 ) && ( ( LeftSide != nullptr ) || ( RightSide != nullptr ) );

// The dragging window flag is enabled only if it's an empty space.
if( TabIndex == -1 )
{
m_IsDraggingWindow = true;
m_DragWindowOffsetPos = static_cast< CTabbedWindow* >( parent()->parent()->parent() )->mapFromGlobal( QCursor::pos() );
}
else
{
m_IsDraggingWindow = false;
}
}
}

void CTabbedWindowTabBar::mouseReleaseEvent( QMouseEvent* event )
{
// Base class.
QTabBar::mouseReleaseEvent( event );

// Set the window properties.
if( m_DragWindow != nullptr )
{
m_DragWindow->setWindowOpacity( 1.0 );
m_DragWindow->setAttribute( Qt::WA_TransparentForMouseEvents, false );
}

// Reset states.
m_IsDraggingWindow = false;
m_DragWindowOffsetPos = QPoint( 0, 0 );
m_IsDraggingTab = false;
m_DragWindow = nullptr;
}

void CTabbedWindowTabBar::mouseDoubleClickEvent( QMouseEvent* event )
{
// Action only valid with the left button.
if( event->button() == Qt::LeftButton )
{
// Check if the double click is on an empty space.
if( tabAt( event->pos() ) == -1 )
{
// Get the window.
CTabbedWindow* TabbedWindow = static_cast< CTabbedWindow* >( parent()->parent()->parent() );

// Change the maximize state.
if( TabbedWindow->isMaximized() )
TabbedWindow->showNormal();
else
TabbedWindow->showMaximized();

// Accept the event.
event->accept();
}
}
}