00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "nativetabbar_p.h"
00023
00024
00025 #include <QIcon>
00026 #include <QMouseEvent>
00027 #include <QPainter>
00028 #include <QApplication>
00029 #include <QStyleOption>
00030 #include <QToolButton>
00031
00032 #include <QGradient>
00033 #include <QLinearGradient>
00034
00035
00036 #include <kdebug.h>
00037 #include <kcolorutils.h>
00038 #include <kicon.h>
00039 #include <kiconloader.h>
00040
00041 #include "plasma/plasma.h"
00042 #include "plasma/theme.h"
00043 #include "plasma/animator.h"
00044 #include "plasma/framesvg.h"
00045 #include "plasma/paintutils.h"
00046
00047
00048
00049 namespace Plasma
00050 {
00051
00052 class NativeTabBarPrivate
00053 {
00054 public:
00055 NativeTabBarPrivate(NativeTabBar *parent)
00056 : q(parent),
00057 shape(NativeTabBar::RoundedNorth),
00058 backgroundSvg(0),
00059 buttonSvg(0),
00060 closeIcon("window-close"),
00061 animationId(-1),
00062 mousePressOffset(0)
00063 {
00064 }
00065
00066 ~NativeTabBarPrivate()
00067 {
00068 delete backgroundSvg;
00069 delete buttonSvg;
00070 }
00071
00072 void syncBorders();
00073 void storeLastIndex();
00074
00075 NativeTabBar *q;
00076 QTabBar::Shape shape;
00077 FrameSvg *backgroundSvg;
00078 qreal left, top, right, bottom;
00079 FrameSvg *buttonSvg;
00080 qreal buttonLeft, buttonTop, buttonRight, buttonBottom;
00081 KIcon closeIcon;
00082
00083 int animationId;
00084
00085 QRect currentAnimRect;
00086 QRect startAnimRect;
00087 int mousePressOffset;
00088 int lastIndex[2];
00089 qreal animProgress;
00090 };
00091
00092 void NativeTabBarPrivate::syncBorders()
00093 {
00094 backgroundSvg->getMargins(left, top, right, bottom);
00095 buttonSvg->getMargins(buttonLeft, buttonTop, buttonRight, buttonBottom);
00096 }
00097
00098 void NativeTabBarPrivate::storeLastIndex()
00099 {
00100
00101 if (lastIndex[0] == -1) {
00102 lastIndex[1] = q->currentIndex();
00103 }
00104 lastIndex[0] = lastIndex[1];
00105 lastIndex[1] = q->currentIndex();
00106 }
00107
00108 NativeTabBar::NativeTabBar(QWidget *parent)
00109 : KTabBar(parent),
00110 d(new NativeTabBarPrivate(this))
00111 {
00112 d->backgroundSvg = new Plasma::FrameSvg();
00113 d->backgroundSvg->setImagePath("widgets/frame");
00114 d->backgroundSvg->setElementPrefix("sunken");
00115
00116 d->buttonSvg = new Plasma::FrameSvg();
00117 d->buttonSvg->setImagePath("widgets/button");
00118 d->buttonSvg->setElementPrefix("normal");
00119
00120 d->syncBorders();
00121
00122 d->lastIndex[0] = -1;
00123 connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
00124
00125 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
00126 }
00127
00128 NativeTabBar::~NativeTabBar()
00129 {
00130 delete d;
00131 }
00132
00133 QRect NativeTabBar::tabRect(int index) const
00134 {
00135 QRect rect = KTabBar::tabRect(index).translated(d->left, d->top);
00136
00137 if (isVertical()) {
00138 rect.setWidth(width() - d->left - d->right);
00139
00140 if (index == count() - 1) {
00141 rect.adjust(0, 0, 0, -d->bottom);
00142 }
00143 } else {
00144 rect.setHeight(height() - d->top- d->bottom);
00145
00146 if (index == count() - 1) {
00147 rect.adjust(0, 0, -d->right, 0);
00148 }
00149 }
00150
00151 return rect;
00152 }
00153
00154 int NativeTabBar::lastIndex() const
00155 {
00156 return d->lastIndex[0];
00157 }
00158
00159 QSize NativeTabBar::tabSizeHint(int index) const
00160 {
00161
00162 QSize hint = tabSize(index);
00163 int minwidth = 0;
00164 int minheight = 0;
00165 int maxwidth = 0;
00166
00167 Shape s = shape();
00168 switch (s) {
00169 case RoundedSouth:
00170 case TriangularSouth:
00171 case RoundedNorth:
00172 case TriangularNorth:
00173 if (count() > 0) {
00174 for (int i = count() - 1; i >= 0; i--) {
00175 minwidth += tabSize(i).width();
00176 }
00177
00178 if (minwidth < width() - d->left - d->right) {
00179 hint.rwidth() += (width() - d->left - d->right - minwidth) / count();
00180 }
00181 }
00182 break;
00183 case RoundedWest:
00184 case TriangularWest:
00185 case RoundedEast:
00186 case TriangularEast:
00187 if (count() > 0) {
00188 for (int i = count() - 1; i >= 0; i--) {
00189 minheight += tabSize(i).height();
00190 if (tabSize(i).width() > maxwidth) {
00191 maxwidth = tabSize(i).width();
00192 }
00193 }
00194
00195 if (minheight < height()) {
00196 hint.rheight() += (height() - minheight) / count();
00197 }
00198 }
00199 break;
00200 }
00201 return hint;
00202 }
00203
00204
00205 QSize NativeTabBar::sizeHint() const
00206 {
00207 return KTabBar::sizeHint();
00208 }
00209
00210 void NativeTabBar::paintEvent(QPaintEvent *event)
00211 {
00212 if (!styleSheet().isNull()) {
00213 KTabBar::paintEvent(event);
00214 return;
00215 }
00216
00217 QPainter painter(this);
00218
00219
00220
00221 if (drawBase()) {
00222 d->backgroundSvg->paintFrame(&painter);
00223 }
00224
00225
00226 QRect movingRect;
00227
00228 if (d->currentAnimRect.isNull()) {
00229 movingRect = tabRect(currentIndex());
00230 } else {
00231 movingRect = d->currentAnimRect;
00232 }
00233
00234
00235 d->buttonSvg->resizeFrame(movingRect.size());
00236 d->buttonSvg->paintFrame(&painter, movingRect.topLeft());
00237
00238 QFontMetrics metrics(painter.font());
00239
00240 for (int i = 0; i < count(); ++i) {
00241 QRect rect = tabRect(i).adjusted(d->buttonLeft, d->buttonTop,
00242 -d->buttonRight, -d->buttonBottom);
00243
00244 QRect iconRect = QRect(rect.x(), rect.y(), iconSize().width(), iconSize().height());
00245
00246 iconRect.moveCenter(QPoint(iconRect.center().x(), rect.center().y()));
00247 tabIcon(i).paint(&painter, iconRect);
00248
00249
00250 if (i == currentIndex() && d->animProgress == 1) {
00251 painter.setPen(Plasma::Theme::defaultTheme()->color(Theme::ButtonTextColor));
00252 } else {
00253 QColor color(Plasma::Theme::defaultTheme()->color(Theme::TextColor));
00254 if (!isTabEnabled(i)) {
00255 color.setAlpha(140);
00256 }
00257
00258 painter.setPen(color);
00259 }
00260 QRect textRect = rect;
00261
00262 if (!tabIcon(i).isNull()) {
00263 textRect.setLeft(iconRect.right());
00264 }
00265
00266 painter.setFont(Plasma::Theme::defaultTheme()->font(Plasma::Theme::DefaultFont));
00267 painter.drawText(textRect, Qt::AlignCenter | Qt::TextHideMnemonic, tabText(i));
00268
00269 if (isCloseButtonEnabled()) {
00270 d->closeIcon.paint(&painter, QRect(closeButtonPos(i), QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)) );
00271 }
00272 }
00273
00274
00275 QRect scrollButtonsRect;
00276 foreach (QObject *child, children()) {
00277 QToolButton *childWidget = qobject_cast<QToolButton *>(child);
00278 if (childWidget) {
00279 if (!childWidget->isVisible()) {
00280 continue;
00281 }
00282
00283 if (scrollButtonsRect.isValid()) {
00284 scrollButtonsRect = scrollButtonsRect.united(childWidget->geometry());
00285 } else {
00286 scrollButtonsRect = childWidget->geometry();
00287 }
00288 }
00289 }
00290
00291 if (scrollButtonsRect.isValid()) {
00292 scrollButtonsRect.adjust(2, 4, -2, -4);
00293 painter.save();
00294
00295 QColor background(Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor));
00296 background.setAlphaF(0.75);
00297
00298 painter.setRenderHint(QPainter::Antialiasing);
00299 painter.fillPath(PaintUtils::roundedRectangle(scrollButtonsRect, 5), background);
00300 painter.restore();
00301
00302 QStyleOption so;
00303 so.initFrom(this);
00304 so.palette.setColor(QPalette::ButtonText,
00305 Plasma::Theme::defaultTheme()->color(Theme::TextColor));
00306
00307 so.rect = scrollButtonsRect.adjusted(0, 0, -scrollButtonsRect.width() / 2, 0);
00308 style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &so, &painter, this);
00309
00310 so.rect = scrollButtonsRect.adjusted(scrollButtonsRect.width() / 2, 0, 0, 0);
00311 style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &so, &painter, this);
00312 }
00313 }
00314
00315 void NativeTabBar::resizeEvent(QResizeEvent *event)
00316 {
00317 KTabBar::resizeEvent(event);
00318 d->currentAnimRect = tabRect(currentIndex());
00319 d->backgroundSvg->resizeFrame(size());
00320 d->syncBorders();
00321
00322 update();
00323 }
00324
00325 void NativeTabBar::tabInserted(int index)
00326 {
00327 KTabBar::tabInserted(index);
00328 emit sizeHintChanged();
00329 }
00330
00331 void NativeTabBar::tabRemoved(int index)
00332 {
00333 KTabBar::tabRemoved(index);
00334 emit sizeHintChanged();
00335 }
00336
00337 void NativeTabBar::tabLayoutChange()
00338 {
00339 KTabBar::tabLayoutChange();
00340
00341 if (shape() != d->shape) {
00342 d->shape = shape();
00343 emit shapeChanged(d->shape);
00344 }
00345 }
00346
00347 void NativeTabBar::startAnimation()
00348 {
00349 d->storeLastIndex();
00350 Plasma::Animator::self()->customAnimation(
00351 10, 150, Plasma::Animator::EaseInOutCurve, this, "onValueChanged");
00352 }
00353
00354 void NativeTabBar::onValueChanged(qreal value)
00355 {
00356 if ((d->animProgress = value) == 1.0) {
00357 animationFinished();
00358 return;
00359 }
00360
00361
00362 QRect rect = tabRect(currentIndex());
00363 QRect lastRect = d->startAnimRect.isNull() ? tabRect(lastIndex())
00364 : d->startAnimRect;
00365 int x = isHorizontal() ? (int)(lastRect.x() - value * (lastRect.x() - rect.x())) : rect.x();
00366 int y = isHorizontal() ? rect.y() : (int)(lastRect.y() - value * (lastRect.y() - rect.y()));
00367 QSizeF sz = lastRect.size() - value * (lastRect.size() - rect.size());
00368 d->currentAnimRect = QRect(x, y, (int)(sz.width()), (int)(sz.height()));
00369 update();
00370 }
00371
00372 void NativeTabBar::animationFinished()
00373 {
00374 d->startAnimRect = QRect();
00375 d->currentAnimRect = QRect();
00376 update();
00377 }
00378
00379 bool NativeTabBar::isVertical() const
00380 {
00381 Shape s = shape();
00382 if(s == RoundedWest ||
00383 s == RoundedEast ||
00384 s == TriangularWest ||
00385 s == TriangularEast) {
00386 return true;
00387 }
00388 return false;
00389 }
00390
00391 bool NativeTabBar::isHorizontal() const
00392 {
00393 return !isVertical();
00394 }
00395
00396 QSize NativeTabBar::tabSize(int index) const
00397 {
00398 QSize hint;
00399 const QFontMetrics metrics(QApplication::font());
00400 const QSize textSize = metrics.size(Qt::TextHideMnemonic, tabText(index));
00401 hint.rwidth() = textSize.width() + iconSize().width();
00402 hint.rheight() = qMax(iconSize().height(), textSize.height());
00403 hint.rwidth() += d->buttonLeft + d->buttonRight;
00404 hint.rheight() += d->buttonTop + d->buttonBottom;
00405
00406 if (isVertical()) {
00407 hint.rwidth() = qMax(hint.width(), int(minimumWidth() - d->left - d->right));
00408 } else {
00409 hint.rheight() = qMax(hint.height(), int(minimumHeight() - d->top - d->bottom));
00410 }
00411
00412 return hint;
00413 }
00414
00415
00416 QPoint NativeTabBar::closeButtonPos( int tabIndex ) const
00417 {
00418 QPoint buttonPos;
00419 if ( tabIndex < 0 ) {
00420 return buttonPos;
00421 }
00422
00423 int availableHeight = height();
00424 if ( tabIndex == currentIndex() ) {
00425 QStyleOption option;
00426 option.initFrom(this);
00427 availableHeight -= style()->pixelMetric( QStyle::PM_TabBarTabShiftVertical, &option, this );
00428 }
00429
00430 const QRect tabBounds = tabRect( tabIndex );
00431 const int xInc = (height() - KIconLoader::SizeSmall) / 2;
00432
00433 if ( layoutDirection() == Qt::RightToLeft ) {
00434 buttonPos = tabBounds.topLeft();
00435 buttonPos.rx() += xInc;
00436 } else {
00437 buttonPos = tabBounds.topRight();
00438 buttonPos.rx() -= KIconLoader::SizeSmall + xInc;
00439 }
00440 buttonPos.ry() += (availableHeight - KIconLoader::SizeSmall) / 2;
00441
00442 return buttonPos;
00443 }
00444
00445 void NativeTabBar::mousePressEvent(QMouseEvent *event)
00446 {
00447 if (d->currentAnimRect.isNull()) {
00448 QRect rect = tabRect(currentIndex());
00449
00450 if (rect.contains(event->pos())) {
00451 d->mousePressOffset = event->pos().x();
00452 event->accept();
00453 return;
00454 }
00455 }
00456
00457 KTabBar::mousePressEvent(event);
00458 }
00459
00460 void NativeTabBar::mouseMoveEvent(QMouseEvent *event)
00461 {
00462 if (d->mousePressOffset) {
00463 d->currentAnimRect = tabRect(currentIndex());
00464
00465 int pos = qBound(0, d->currentAnimRect.left() + (event->pos().x() - d->mousePressOffset),
00466 width() - d->currentAnimRect.width());
00467 d->currentAnimRect.moveLeft(pos);
00468 update();
00469 } else {
00470 KTabBar::mouseMoveEvent(event);
00471 }
00472 }
00473
00474 void NativeTabBar::mouseReleaseEvent(QMouseEvent *event)
00475 {
00476 if (d->mousePressOffset) {
00477 bool left = event->pos().x() - d->mousePressOffset < 0;
00478 int index = tabAt(QPoint(left ? d->currentAnimRect.left() : d->currentAnimRect.right(), 1));
00479 d->mousePressOffset = 0;
00480
00481 if (index != currentIndex() && isTabEnabled(index)) {
00482 d->startAnimRect = d->currentAnimRect;
00483 setCurrentIndex(index);
00484 } else {
00485 d->currentAnimRect = QRect();
00486 }
00487
00488 update();
00489 } else {
00490 KTabBar::mouseReleaseEvent(event);
00491 }
00492 }
00493
00494 void NativeTabBar::wheelEvent(QWheelEvent *event)
00495 {
00496 if (underMouse()) {
00497
00498 if (event->delta() < 0) {
00499 int index = currentIndex();
00500
00501 for (int i = 0; i < count()-1; ++i) {
00502 index = (index + 1) % count();
00503 if (isTabEnabled(index)) {
00504 break;
00505 }
00506 }
00507
00508 setCurrentIndex(index);
00509 } else {
00510 int index = currentIndex();
00511 for (int i = 0; i < count()-1; ++i) {
00512 index = (count() + index -1) % count();
00513 if (isTabEnabled(index)) {
00514 break;
00515 }
00516 }
00517
00518 setCurrentIndex(index);
00519 }
00520 } else {
00521 QTabBar::wheelEvent(event);
00522 }
00523 }
00524
00525 }
00526
00527 #include "nativetabbar_p.moc"
00528