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