00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "ui/tabbar.h"
00022
00023
00024 #include <KGlobalSettings>
00025
00026
00027 #include <QIcon>
00028 #include <QMouseEvent>
00029 #include <QPainter>
00030
00031 #include <QGradient>
00032 #include <QLinearGradient>
00033
00034 #include "plasma/plasma.h"
00035 #include "plasma/animator.h"
00036
00037 using namespace Kickoff;
00038
00039 TabBar::TabBar(QWidget *parent)
00040 : QTabBar(parent),
00041 m_hoveredTabIndex(-1),
00042 m_switchOnHover(true),
00043 m_animateSwitch(true)
00044 {
00045 m_lastIndex[0] = -1;
00046 connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
00047
00048 m_tabSwitchTimer.setSingleShot(true);
00049 connect(&m_tabSwitchTimer, SIGNAL(timeout()), this, SLOT(switchToHoveredTab()));
00050 setMouseTracking(true);
00051 setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );
00052 setUsesScrollButtons( false );
00053 }
00054
00055 void TabBar::setShape( Shape shape )
00056 {
00057 resize( 0, 0 );
00058
00059
00060 QTabBar::setShape( shape );
00061 }
00062
00063 void TabBar::setCurrentIndexWithoutAnimation(int index)
00064 {
00065 disconnect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
00066 setCurrentIndex(index);
00067 storeLastIndex();
00068 connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
00069 animationFinished();
00070 }
00071
00072 void TabBar::setSwitchTabsOnHover(bool switchOnHover)
00073 {
00074 m_switchOnHover = switchOnHover;
00075 }
00076
00077 bool TabBar::switchTabsOnHover() const
00078 {
00079 return m_switchOnHover;
00080 }
00081
00082 void TabBar::setAnimateSwitch(bool animateSwitch)
00083 {
00084 m_animateSwitch = animateSwitch;
00085 }
00086
00087 bool TabBar::animateSwitch()
00088 {
00089 return m_animateSwitch;
00090 }
00091
00092 QSize TabBar::tabSize(int index) const
00093 {
00094 QSize hint;
00095 const QFontMetrics metrics(KGlobalSettings::smallestReadableFont());
00096 const QSize textSize = metrics.size(Qt::TextHideMnemonic, tabText(index));
00097 hint.rwidth() = qMax(iconSize().width(), textSize.width());
00098 hint.rheight() = iconSize().height() + textSize.height();
00099 hint.rwidth() += 4 * TAB_CONTENTS_MARGIN;
00100 hint.rheight() += 2 * TAB_CONTENTS_MARGIN;
00101 return hint;
00102 }
00103
00104 void TabBar::storeLastIndex()
00105 {
00106
00107 if (m_lastIndex[0] == -1) {
00108 m_lastIndex[1] = currentIndex();
00109 }
00110 m_lastIndex[0] = m_lastIndex[1];
00111 m_lastIndex[1] = currentIndex();
00112 }
00113
00114 int TabBar::lastIndex() const
00115 {
00116 return m_lastIndex[0];
00117 }
00118
00119 QSize TabBar::tabSizeHint(int index) const
00120 {
00121 QSize hint = tabSize(index);
00122 int minwidth = 0;
00123 int minheight = 0;
00124
00125 Shape s = shape();
00126 switch (s) {
00127 case RoundedSouth:
00128 case TriangularSouth:
00129 case RoundedNorth:
00130 case TriangularNorth:
00131 if (count() > 0) {
00132 for (int i = count() - 1; i >= 0; i--) {
00133 minwidth += tabSize(i).width();
00134 }
00135 if (minwidth < width()) {
00136 hint.rwidth() += (width() - minwidth) / count();
00137 }
00138 }
00139 break;
00140 case RoundedWest:
00141 case TriangularWest:
00142 case RoundedEast:
00143 case TriangularEast:
00144 if (count() > 0) {
00145 for (int i = count() - 1; i >= 0; i--) {
00146 minheight += tabSize(i).height();
00147 }
00148 if (minheight < height()) {
00149 hint.rheight() += (height() - minheight) / count();
00150 }
00151 }
00152 hint.rwidth() = qMax( hint.width(), width() );
00153 break;
00154 }
00155 return hint;
00156 }
00157
00158 QPainterPath TabBar::tabPath(const QRect &_r)
00159 {
00160 const int radius = 6;
00161 Shape s = shape();
00162 QPainterPath path;
00163 QRect r = _r;
00164
00165 switch (s) {
00166 case RoundedSouth:
00167 case TriangularSouth:
00168 r.adjust(0, 0, 0, -3);
00169 path.moveTo(rect().topLeft());
00170 path.lineTo(r.topLeft());
00171
00172 path.quadTo(r.topLeft() + QPoint(radius, 0), r.topLeft() + QPoint(radius, radius));
00173 path.lineTo(r.bottomLeft() + QPoint(radius, -radius));
00174
00175 path.quadTo(r.bottomLeft() + QPoint(radius, 0), r.bottomLeft() + QPoint(radius * 2, 0));
00176 path.lineTo(r.bottomRight() + QPoint(-radius * 2, 0));
00177
00178 path.quadTo(r.bottomRight() + QPoint(-radius, 0), r.bottomRight() + QPoint(-radius, -radius));
00179 path.lineTo(r.topRight() + QPoint(-radius, radius));
00180
00181 path.quadTo(r.topRight() + QPoint(-radius, 0), r.topRight());
00182 path.lineTo(rect().topRight());
00183 break;
00184 case RoundedNorth:
00185 case TriangularNorth:
00186 r.adjust(0, 3, 0, 0);
00187 path.moveTo(rect().bottomLeft());
00188
00189 path.lineTo(r.bottomLeft());
00190 path.quadTo(r.bottomLeft() + QPoint(radius, 0), r.bottomLeft() + QPoint(radius, -radius));
00191
00192 path.lineTo(r.topLeft() + QPoint(radius, radius));
00193 path.quadTo(r.topLeft() + QPoint(radius, 0), r.topLeft() + QPoint(radius * 2, 0));
00194
00195 path.lineTo(r.topRight() + QPoint(-radius * 2, 0));
00196 path.quadTo(r.topRight() + QPoint(-radius, 0), r.topRight() + QPoint(-radius, radius));
00197
00198 path.lineTo(r.bottomRight() + QPoint(-radius, -radius));
00199 path.quadTo(r.bottomRight() + QPoint(-radius, 0), r.bottomRight());
00200 path.lineTo(rect().bottomRight());
00201 break;
00202 case RoundedWest:
00203 case TriangularWest:
00204 r.adjust(3, 0, 0, 0);
00205 path.moveTo(rect().topRight());
00206
00207 path.lineTo(r.topRight());
00208 path.quadTo(r.topRight() + QPoint(0, radius), r.topRight() + QPoint(-radius, radius));
00209
00210 path.lineTo(r.topLeft() + QPoint(radius, radius));
00211 path.quadTo(r.topLeft() + QPoint(0, radius), r.topLeft() + QPoint(0, radius * 2));
00212
00213 path.lineTo(r.bottomLeft() + QPoint(0, -radius * 2));
00214 path.quadTo(r.bottomLeft() + QPoint(0, -radius), r.bottomLeft() + QPoint(radius, -radius));
00215
00216 path.lineTo(r.bottomRight() + QPoint(-radius, -radius));
00217 path.quadTo(r.bottomRight() + QPoint(0, -radius), r.bottomRight());
00218 path.lineTo(rect().bottomRight());
00219 break;
00220 case RoundedEast:
00221 case TriangularEast:
00222 r.adjust(0, 0, -3, 0);
00223 path.moveTo(rect().topLeft());
00224
00225 path.lineTo(r.topLeft());
00226 path.quadTo(r.topLeft() + QPoint(0, radius), r.topLeft() + QPoint(radius, radius));
00227
00228 path.lineTo(r.topRight() + QPoint(-radius, radius));
00229 path.quadTo(r.topRight() + QPoint(0, radius), r.topRight() + QPoint(0, radius * 2));
00230
00231 path.lineTo(r.bottomRight() + QPoint(0, -radius * 2));
00232 path.quadTo(r.bottomRight() + QPoint(0, -radius), r.bottomRight() + QPoint(-radius, -radius));
00233
00234 path.lineTo(r.bottomLeft() + QPoint(radius, -radius));
00235 path.quadTo(r.bottomLeft() + QPoint(0, -radius), r.bottomLeft());
00236 path.lineTo(rect().bottomLeft());
00237 break;
00238 }
00239
00240 return path;
00241 }
00242
00243 void TabBar::paintEvent(QPaintEvent *event)
00244 {
00245 Q_UNUSED(event)
00246 QPainter painter(this);
00247
00248 int currentTab = currentIndex();
00249
00250 painter.setFont(KGlobalSettings::smallestReadableFont());
00251
00252
00253 QRect movingRect;
00254
00255 if (m_currentAnimRect.isNull()) {
00256 movingRect = tabRect(currentIndex());
00257 } else {
00258 movingRect = m_currentAnimRect;
00259 }
00260 QPainterPath path = tabPath(movingRect);
00261
00262 painter.save();
00263 painter.setPen(QPen(palette().base(), 1));
00264
00265
00266 switch (shape()) {
00267 case RoundedSouth:
00268 case TriangularSouth:
00269 painter.drawLine(rect().topLeft(), rect().topRight());
00270 break;
00271 case RoundedNorth:
00272 case TriangularNorth:
00273 painter.drawLine(rect().bottomLeft(), rect().bottomRight());
00274 break;
00275 case RoundedWest:
00276 case TriangularWest:
00277 painter.drawLine(rect().topRight(), rect().bottomRight());
00278 break;
00279 case RoundedEast:
00280 case TriangularEast:
00281 painter.drawLine(rect().topLeft(), rect().bottomLeft());
00282 break;
00283 }
00284
00285 painter.translate(0.5, 0.5);
00286 painter.setRenderHint(QPainter::Antialiasing);
00287 painter.fillPath(path, palette().base());
00288 painter.setPen(QPen(palette().mid(), 1));
00289 painter.drawPath(path);
00290 painter.restore();
00291
00292 QFontMetrics metrics(painter.font());
00293 int textHeight = metrics.height();
00294
00295 for (int i = 0; i < count(); i++) {
00296 QRect rect = tabRect(i).adjusted(TAB_CONTENTS_MARGIN, TAB_CONTENTS_MARGIN,
00297 -TAB_CONTENTS_MARGIN, -TAB_CONTENTS_MARGIN);
00298
00299 QRect iconRect = rect;
00300 iconRect.setBottom(iconRect.bottom() - textHeight);
00301 iconRect.adjust(0, (isVertical() ? 1 : 0) * TAB_CONTENTS_MARGIN + 3, 0, 0);
00302 tabIcon(i).paint(&painter, iconRect);
00303
00304
00305 QRect textRect = rect;
00306 textRect.setTop(textRect.bottom() - textHeight);
00307 painter.drawText(textRect, Qt::AlignCenter | Qt::TextHideMnemonic, tabText(i));
00308 }
00309 }
00310
00311 void TabBar::leaveEvent(QEvent *event)
00312 {
00313 Q_UNUSED(event)
00314 m_hoveredTabIndex = -1;
00315 }
00316
00317 void TabBar::mouseMoveEvent(QMouseEvent *event)
00318 {
00319 m_hoveredTabIndex = tabAt(event->pos());
00320 if (m_switchOnHover && m_hoveredTabIndex > -1 && m_hoveredTabIndex != currentIndex()) {
00321 m_tabSwitchTimer.stop();
00322 m_tabSwitchTimer.start(50);
00323 }
00324 }
00325
00326 void TabBar::resizeEvent(QResizeEvent* event)
00327 {
00328 QTabBar::resizeEvent(event);
00329 m_currentAnimRect = tabRect(currentIndex());
00330 update();
00331 }
00332
00333 void TabBar::switchToHoveredTab()
00334 {
00335 if (m_hoveredTabIndex < 0 || m_hoveredTabIndex == currentIndex()) {
00336 return;
00337 }
00338
00339 if (m_animateSwitch) {
00340 setCurrentIndex(m_hoveredTabIndex);
00341 }
00342 else {
00343 setCurrentIndexWithoutAnimation(m_hoveredTabIndex);
00344 }
00345 }
00346
00347 void TabBar::startAnimation()
00348 {
00349 storeLastIndex();
00350 Plasma::Animator::self()->customAnimation(10, 150, Plasma::Animator::EaseInOutCurve, this, "onValueChanged");
00351
00352 }
00353
00354 void TabBar::onValueChanged(qreal value)
00355 {
00356 if ((m_animProgress = value) == 1.0) {
00357 animationFinished();
00358 return;
00359 }
00360
00361
00362 QRect rect = tabRect(currentIndex());
00363 QRect lastRect = tabRect(lastIndex());
00364 int x = isHorizontal() ? (int)(lastRect.x() - value * (lastRect.x() - rect.x())) : rect.x();
00365 int y = isHorizontal() ? rect.y() : (int)(lastRect.y() - value * (lastRect.y() - rect.y()));
00366 QSizeF sz = lastRect.size() - value * (lastRect.size() - rect.size());
00367 m_currentAnimRect = QRect(x, y, (int)(sz.width()), (int)(sz.height()));
00368 update();
00369 }
00370
00371 void TabBar::animationFinished()
00372 {
00373 m_currentAnimRect = QRect();
00374 update();
00375 }
00376
00377 bool TabBar::isVertical() const
00378 {
00379 Shape s = shape();
00380 if( s == RoundedWest ||
00381 s == RoundedEast ||
00382 s == TriangularWest ||
00383 s == TriangularEast ) {
00384 return true;
00385 }
00386 return false;
00387 }
00388
00389 bool TabBar::isHorizontal() const
00390 {
00391 return !isVertical();
00392 }
00393
00394 #include "tabbar.moc"