From ea5991e330282a5b18301d653ccb5a7d960a7db5 Mon Sep 17 00:00:00 2001 From: Jens Bache-Wiig Date: Wed, 15 Apr 2009 14:46:59 +0200 Subject: Fixed painting issues with draggable tabs This fixes a few of the remaining glitches tabbar animations have: * We no longer grab tabs but paint them through QStyle. This makes tabs work and animate correctly when they are outside the visible region. * Buttons now correctly follow tabs when dropped * Gtkstyle recieved some polish to make it look more native. Task-number: 247694, 251166 Reviewed-by: nrc --- src/gui/styles/qgtkstyle.cpp | 9 +++-- src/gui/widgets/qtabbar.cpp | 90 +++++++++++++++++++++++--------------------- src/gui/widgets/qtabbar_p.h | 33 ++-------------- 3 files changed, 57 insertions(+), 75 deletions(-) diff --git a/src/gui/styles/qgtkstyle.cpp b/src/gui/styles/qgtkstyle.cpp index 519fed7..541415f 100644 --- a/src/gui/styles/qgtkstyle.cpp +++ b/src/gui/styles/qgtkstyle.cpp @@ -2760,10 +2760,13 @@ void QGtkStyle::drawControl(ControlElement element, if (tab->state & State_Selected) state = GTK_STATE_NORMAL; - bool first = tab->position == QStyleOptionTab::Beginning || tab->position == QStyleOptionTab::OnlyOneTab; - bool last = tab->position == QStyleOptionTab::End || tab->position == QStyleOptionTab::OnlyOneTab; bool selected = (tab->state & State_Selected); - if (option->direction == Qt::RightToLeft) { + bool first = false, last = false; + if (widget) { + // This is most accurate and avoids resizing tabs while moving + first = tab->rect.left() == widget->rect().left(); + last = tab->rect.right() == widget->rect().right(); + } else if (option->direction == Qt::RightToLeft) { bool tmp = first; first = last; last = tmp; diff --git a/src/gui/widgets/qtabbar.cpp b/src/gui/widgets/qtabbar.cpp index 7d970ad..b6e6b6d 100644 --- a/src/gui/widgets/qtabbar.cpp +++ b/src/gui/widgets/qtabbar.cpp @@ -131,7 +131,7 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const option->state &= ~QStyle::State_Enabled; if (isActiveWindow()) option->state |= QStyle::State_Active; - if (option->rect == d->hoverRect) + if (!d->dragInProgress && option->rect == d->hoverRect) option->state |= QStyle::State_MouseOver; option->shape = d->shape; option->text = tab.text; @@ -454,9 +454,6 @@ void QTabBarPrivate::layoutTabs() maxExtent = maxWidth; } - if (pressedIndex != -1 && movable) - grabCache(0, tabList.count(), true); - Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure. // Mirror our front item. tabChain[tabChainIndex].init(); @@ -1484,6 +1481,8 @@ void QTabBar::paintEvent(QPaintEvent *) bool vertical = verticalTabs(d->shape); QStyleOptionTab cutTab; selected = d->currentIndex; + if (d->dragInProgress) + selected = d->pressedIndex; for (int i = 0; i < d->tabList.count(); ++i) optTabBase.tabBarRect |= tabRect(i); @@ -1522,11 +1521,7 @@ void QTabBar::paintEvent(QPaintEvent *) if (i == selected) continue; - if (!d->tabList[i].animatingCache.isNull() && d->paintWithOffsets) { - p.drawPixmap(tab.rect, d->tabList[i].animatingCache); - } else { - p.drawControl(QStyle::CE_TabBarTab, tab); - } + p.drawControl(QStyle::CE_TabBarTab, tab); } // Draw the selected tab last to get it "on top" @@ -1539,7 +1534,11 @@ void QTabBar::paintEvent(QPaintEvent *) else tab.rect.moveLeft(tab.rect.x() + d->tabList[selected].dragOffset); } - p.drawControl(QStyle::CE_TabBarTab, tab); + if (!d->dragInProgress) + p.drawControl(QStyle::CE_TabBarTab, tab); + else + d->movingTab->setGeometry(tab.rect); + } // Only draw the tear indicator if necessary. Most of the time we don't need too. @@ -1680,6 +1679,7 @@ void QTabBarPrivate::_q_moveTab(int offset) if (!validIndex(index)) return; tabList[index].dragOffset = offset; + layoutTab(index); // Make buttons follow tab q->update(); } } @@ -1727,8 +1727,7 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event) if (!d->dragInProgress && d->pressedIndex != -1) { if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) { d->dragInProgress = true; - if (d->animations.isEmpty()) - d->grabCache(0, d->tabList.count(), false); + d->setupMovableTab(); } } @@ -1773,7 +1772,6 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event) if (dragDistance > needsToBeOver) d->slide(i + offset, d->pressedIndex); } - } // Buttons needs to follow the dragged tab d->layoutTab(d->pressedIndex); @@ -1801,32 +1799,41 @@ void QTabBarPrivate::_q_moveTabFinished() } } -void QTabBarPrivate::grabCache(int start, int end, bool unhide) +void QTabBarPrivate::setupMovableTab() { Q_Q(QTabBar); - paintWithOffsets = false; - bool showButtonsAgain = rightB->isVisible(); - rightB->hide(); - leftB->hide(); - - QWidget *topLevel = q->window(); - QPoint topLevelOffset(q->mapTo(topLevel, QPoint())); - for (int i = start; i < end; ++i) { - QRect tabRect = q->tabRect(i); - tabRect.translate(topLevelOffset); - if (unhide) { - tabList[i].unHideWidgets(); - layoutWidgets(i); - } - tabList[i].animatingCache = QPixmap::grabWidget(topLevel, tabRect); - if (i != pressedIndex) - tabList[i].hideWidgets(); - } - if (showButtonsAgain) { - rightB->show(); - leftB->show(); - } - paintWithOffsets = true; + if (!movingTab) + movingTab = new QWidget(q); + + QRect grabRect = q->tabRect(pressedIndex); + + QPixmap grabImage(grabRect.size()); + grabImage.fill(Qt::transparent); + QStylePainter p(&grabImage, q); + + QStyleOptionTabV3 tab; + q->initStyleOption(&tab, pressedIndex); + tab.rect.moveTopLeft(QPoint(0, 0)); + p.drawControl(QStyle::CE_TabBarTab, tab); + p.end(); + + QPalette pal; + pal.setBrush(QPalette::All, QPalette::Window, grabImage); + movingTab->setPalette(pal); + movingTab->setGeometry(grabRect); + movingTab->setAutoFillBackground(true); + movingTab->raise(); + + // Re-arrange widget order to avoid overlaps + if (tabList[pressedIndex].leftWidget) + tabList[pressedIndex].leftWidget->raise(); + if (tabList[pressedIndex].rightWidget) + tabList[pressedIndex].rightWidget->raise(); + if (leftB) + leftB->raise(); + if (rightB) + rightB->raise(); + movingTab->setVisible(true); } void QTabBarPrivate::_q_moveTabFinished(int index) @@ -1834,10 +1841,9 @@ void QTabBarPrivate::_q_moveTabFinished(int index) Q_Q(QTabBar); bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index); if (animations.isEmpty() && cleanup) { + movingTab->setVisible(false); // We might not get a mouse release for (int i = 0; i < tabList.count(); ++i) { tabList[i].dragOffset = 0; - tabList[i].unHideWidgets(); - tabList[i].animatingCache = QPixmap(); } if (pressedIndex != -1 && movable) { pressedIndex = -1; @@ -1881,6 +1887,7 @@ void QTabBar::mouseReleaseEvent(QMouseEvent *event) d->_q_moveTabFinished(d->pressedIndex); } d->dragInProgress = false; + d->movingTab->setVisible(false); d->dragStartPosition = QPoint(); } @@ -2203,19 +2210,16 @@ void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget) widget->setParent(this); // make sure our left and right widgets stay on top widget->lower(); + widget->show(); } if (position == LeftSide) { if (d->tabList[index].leftWidget) d->tabList[index].leftWidget->hide(); d->tabList[index].leftWidget = widget; - if(!d->tabList[index].hidLeft && widget) - widget->show(); } else { if (d->tabList[index].rightWidget) d->tabList[index].rightWidget->hide(); d->tabList[index].rightWidget = widget; - if(!d->tabList[index].hidRight && widget) - widget->show(); } d->layoutTabs(); update(); diff --git a/src/gui/widgets/qtabbar_p.h b/src/gui/widgets/qtabbar_p.h index a117aa3..cb1a15b 100644 --- a/src/gui/widgets/qtabbar_p.h +++ b/src/gui/widgets/qtabbar_p.h @@ -70,7 +70,6 @@ QT_BEGIN_NAMESPACE - class QTabBarPrivate : public QWidgetPrivate { Q_DECLARE_PUBLIC(QTabBar) @@ -78,7 +77,7 @@ public: QTabBarPrivate() :currentIndex(-1), pressedIndex(-1), shape(QTabBar::RoundedNorth), - layoutDirty(false), drawBase(true), scrollOffset(0), expanding(true), closeButtonOnTabs(false), selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false) {} + layoutDirty(false), drawBase(true), scrollOffset(0), expanding(true), closeButtonOnTabs(false), selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false), movingTab(0) {} int currentIndex; int pressedIndex; @@ -98,8 +97,6 @@ public: , lastTab(-1) , timeLine(0) , dragOffset(0) - , hidLeft(false) - , hidRight(false) {} bool enabled; int shortcutId; @@ -123,9 +120,6 @@ public: QTimeLine *timeLine; int dragOffset; - QPixmap animatingCache; - bool hidLeft; - bool hidRight; void makeTimeLine(QWidget *q) { if (timeLine) @@ -135,27 +129,6 @@ public: q->connect(timeLine, SIGNAL(finished()), q, SLOT(_q_moveTabFinished())); } - void hideWidgets() { - if (!hidRight && rightWidget) { - hidRight = rightWidget->isVisible(); - rightWidget->hide(); - } - - if (!hidLeft && leftWidget) { - hidLeft = leftWidget->isVisible(); - leftWidget->hide(); - } - } - - void unHideWidgets() { - if (leftWidget && hidLeft) - leftWidget->show(); - hidLeft = false; - if (rightWidget && hidRight) - rightWidget->show(); - hidRight = false; - } - }; QList tabList; QHash animations; @@ -184,12 +157,12 @@ public: void _q_moveTabFinished(int offset); QRect hoverRect; - void grabCache(int start, int end, bool unhide); void refresh(); void layoutTabs(); void layoutWidgets(int index = -1); void layoutTab(int index); void updateMacBorderMetrics(); + void setupMovableTab(); void makeVisible(int index); QSize iconSize; @@ -206,6 +179,8 @@ public: bool dragInProgress; bool documentMode; + QWidget *movingTab; + // shared by tabwidget and qtabbar static void initStyleBaseOption(QStyleOptionTabBarBaseV2 *optTabBase, QTabBar *tabbar, QSize size) { -- cgit v0.12