2008-11-04 00:08:39 +01:00
|
|
|
/*
|
|
|
|
Copyright 2007 Robert Knight <robertknight@gmail.com>
|
|
|
|
Copyright 2008 Marco Martin <notmart@gmail.com>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Own
|
|
|
|
#include "nativetabbar_p.h"
|
|
|
|
|
|
|
|
// Qt
|
|
|
|
#include <QIcon>
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QStyleOption>
|
|
|
|
#include <QToolButton>
|
|
|
|
|
|
|
|
#include <QGradient>
|
|
|
|
#include <QLinearGradient>
|
|
|
|
|
2008-11-04 03:39:56 +01:00
|
|
|
// KDE
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <kcolorutils.h>
|
2009-01-25 18:08:54 +01:00
|
|
|
#include <kicon.h>
|
|
|
|
#include <kiconloader.h>
|
2008-11-04 03:39:56 +01:00
|
|
|
|
2008-11-04 00:08:39 +01:00
|
|
|
#include "plasma/plasma.h"
|
|
|
|
#include "plasma/theme.h"
|
|
|
|
#include "plasma/animator.h"
|
|
|
|
#include "plasma/framesvg.h"
|
|
|
|
#include "plasma/paintutils.h"
|
|
|
|
|
2008-11-15 08:08:30 +01:00
|
|
|
//#include "private/style_p.h"
|
2008-11-04 00:08:39 +01:00
|
|
|
|
|
|
|
namespace Plasma
|
|
|
|
{
|
|
|
|
|
|
|
|
class NativeTabBarPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NativeTabBarPrivate(NativeTabBar *parent)
|
|
|
|
: q(parent),
|
|
|
|
backgroundSvg(0),
|
|
|
|
buttonSvg(0),
|
2009-01-25 14:26:53 +01:00
|
|
|
closeIcon("window-close"),
|
2008-11-04 00:08:39 +01:00
|
|
|
animationId(-1)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~NativeTabBarPrivate()
|
|
|
|
{
|
|
|
|
delete backgroundSvg;
|
|
|
|
delete buttonSvg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void syncBorders();
|
|
|
|
void storeLastIndex();
|
|
|
|
|
|
|
|
NativeTabBar *q;
|
|
|
|
QTabBar::Shape shape; //used to keep track of shape() changes
|
|
|
|
FrameSvg *backgroundSvg;
|
|
|
|
qreal left, top, right, bottom;
|
|
|
|
FrameSvg *buttonSvg;
|
|
|
|
qreal buttonLeft, buttonTop, buttonRight, buttonBottom;
|
2009-01-25 14:26:53 +01:00
|
|
|
KIcon closeIcon;
|
2008-11-04 00:08:39 +01:00
|
|
|
|
|
|
|
int animationId;
|
|
|
|
|
|
|
|
QRect currentAnimRect;
|
|
|
|
int lastIndex[2];
|
|
|
|
qreal animProgress;
|
|
|
|
};
|
|
|
|
|
|
|
|
void NativeTabBarPrivate::syncBorders()
|
|
|
|
{
|
|
|
|
backgroundSvg->getMargins(left, top, right, bottom);
|
|
|
|
buttonSvg->getMargins(buttonLeft, buttonTop, buttonRight, buttonBottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBarPrivate::storeLastIndex()
|
|
|
|
{
|
|
|
|
// if first run
|
|
|
|
if (lastIndex[0] == -1) {
|
|
|
|
lastIndex[1] = q->currentIndex();
|
|
|
|
}
|
|
|
|
lastIndex[0] = lastIndex[1];
|
|
|
|
lastIndex[1] = q->currentIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
NativeTabBar::NativeTabBar(QWidget *parent)
|
2008-11-06 00:28:07 +01:00
|
|
|
: KTabBar(parent),
|
2008-11-04 00:08:39 +01:00
|
|
|
d(new NativeTabBarPrivate(this))
|
|
|
|
{
|
|
|
|
d->backgroundSvg = new Plasma::FrameSvg();
|
|
|
|
d->backgroundSvg->setImagePath("widgets/frame");
|
|
|
|
d->backgroundSvg->setElementPrefix("sunken");
|
|
|
|
|
|
|
|
d->buttonSvg = new Plasma::FrameSvg();
|
|
|
|
d->buttonSvg->setImagePath("widgets/button");
|
|
|
|
d->buttonSvg->setElementPrefix("normal");
|
|
|
|
|
|
|
|
d->syncBorders();
|
|
|
|
|
|
|
|
d->lastIndex[0] = -1;
|
|
|
|
connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
|
|
|
|
|
2009-01-26 14:58:39 +01:00
|
|
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
2008-11-04 00:08:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
NativeTabBar::~NativeTabBar()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect NativeTabBar::tabRect(int index) const
|
|
|
|
{
|
2008-11-07 19:03:16 +01:00
|
|
|
QRect rect = KTabBar::tabRect(index).translated(d->left, d->top);
|
2008-11-04 00:08:39 +01:00
|
|
|
|
2009-01-26 14:58:39 +01:00
|
|
|
if (isVertical()) {
|
|
|
|
rect.setWidth(width()-d->top-d->bottom);
|
|
|
|
} else {
|
|
|
|
rect.setHeight(height()-d->top-d->bottom);
|
|
|
|
}
|
|
|
|
|
2008-11-04 00:08:39 +01:00
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NativeTabBar::lastIndex() const
|
|
|
|
{
|
|
|
|
return d->lastIndex[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize NativeTabBar::tabSizeHint(int index) const
|
|
|
|
{
|
2008-11-07 19:03:16 +01:00
|
|
|
//return KTabBar::tabSizeHint(index);
|
2008-11-04 00:08:39 +01:00
|
|
|
QSize hint = tabSize(index);
|
|
|
|
int minwidth = 0;
|
|
|
|
int minheight = 0;
|
|
|
|
int maxwidth = 0;
|
|
|
|
|
|
|
|
Shape s = shape();
|
|
|
|
switch (s) {
|
|
|
|
case RoundedSouth:
|
|
|
|
case TriangularSouth:
|
|
|
|
case RoundedNorth:
|
|
|
|
case TriangularNorth:
|
|
|
|
if (count() > 0) {
|
|
|
|
for (int i = count() - 1; i >= 0; i--) {
|
|
|
|
minwidth += tabSize(i).width();
|
|
|
|
}
|
|
|
|
|
2008-12-30 19:00:59 +01:00
|
|
|
if (minwidth < width() - d->left - d->right) {
|
|
|
|
hint.rwidth() += (width() - d->left - d->right - minwidth) / count();
|
2008-11-04 00:08:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RoundedWest:
|
|
|
|
case TriangularWest:
|
|
|
|
case RoundedEast:
|
|
|
|
case TriangularEast:
|
|
|
|
if (count() > 0) {
|
|
|
|
for (int i = count() - 1; i >= 0; i--) {
|
|
|
|
minheight += tabSize(i).height();
|
|
|
|
if (tabSize(i).width() > maxwidth) {
|
|
|
|
maxwidth = tabSize(i).width();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (minheight < height()) {
|
|
|
|
hint.rheight() += (height() - minheight) / count();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
|
2009-01-26 14:58:39 +01:00
|
|
|
|
2008-11-04 00:08:39 +01:00
|
|
|
QSize NativeTabBar::sizeHint() const
|
|
|
|
{
|
2009-01-26 14:58:39 +01:00
|
|
|
return KTabBar::sizeHint();
|
2008-11-04 00:08:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::paintEvent(QPaintEvent *event)
|
|
|
|
{
|
|
|
|
if (!styleSheet().isNull()) {
|
2008-11-07 19:03:16 +01:00
|
|
|
KTabBar::paintEvent(event);
|
2008-11-04 00:08:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPainter painter(this);
|
|
|
|
//int numTabs = count();
|
|
|
|
//bool ltr = painter.layoutDirection() == Qt::LeftToRight; // Not yet used
|
|
|
|
|
2008-11-27 19:36:42 +01:00
|
|
|
if (drawBase()) {
|
|
|
|
d->backgroundSvg->paintFrame(&painter);
|
|
|
|
}
|
2008-11-04 00:08:39 +01:00
|
|
|
|
|
|
|
// Drawing Tabborders
|
|
|
|
QRect movingRect;
|
|
|
|
|
|
|
|
if (d->currentAnimRect.isNull()) {
|
|
|
|
movingRect = tabRect(currentIndex());
|
|
|
|
} else {
|
|
|
|
movingRect = d->currentAnimRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
//resizing here because in resizeevent the first time is invalid (still no tabs)
|
|
|
|
d->buttonSvg->resizeFrame(movingRect.size());
|
|
|
|
d->buttonSvg->paintFrame(&painter, movingRect.topLeft());
|
|
|
|
|
|
|
|
QFontMetrics metrics(painter.font());
|
|
|
|
|
|
|
|
for (int i = 0; i < count(); i++) {
|
|
|
|
QRect rect = tabRect(i).adjusted(d->buttonLeft, d->buttonTop,
|
|
|
|
-d->buttonRight, -d->buttonBottom);
|
|
|
|
// draw tab icon
|
|
|
|
QRect iconRect = QRect(rect.x(), rect.y(), iconSize().width(), iconSize().height());
|
|
|
|
|
|
|
|
iconRect.moveCenter(QPoint(iconRect.center().x(), rect.center().y()));
|
|
|
|
tabIcon(i).paint(&painter, iconRect);
|
|
|
|
|
|
|
|
// draw tab text
|
|
|
|
if (i == currentIndex() && d->animProgress == 1) {
|
|
|
|
painter.setPen(Plasma::Theme::defaultTheme()->color(Theme::ButtonTextColor));
|
|
|
|
} else {
|
|
|
|
QColor color(Plasma::Theme::defaultTheme()->color(Theme::TextColor));
|
|
|
|
if (!isTabEnabled(i)) {
|
|
|
|
color.setAlpha(140);
|
|
|
|
}
|
|
|
|
|
|
|
|
painter.setPen(color);
|
|
|
|
}
|
|
|
|
QRect textRect = rect;
|
|
|
|
|
|
|
|
if (!tabIcon(i).isNull()) {
|
|
|
|
textRect.setLeft(iconRect.right());
|
|
|
|
}
|
|
|
|
|
2008-12-21 18:17:35 +01:00
|
|
|
painter.setFont(Plasma::Theme::defaultTheme()->font(Plasma::Theme::DefaultFont));
|
2008-11-04 00:08:39 +01:00
|
|
|
painter.drawText(textRect, Qt::AlignCenter | Qt::TextHideMnemonic, tabText(i));
|
2009-01-25 14:26:53 +01:00
|
|
|
|
|
|
|
if (isCloseButtonEnabled()) {
|
|
|
|
d->closeIcon.paint(&painter, QRect(closeButtonPos(i), QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)) );
|
|
|
|
}
|
2008-11-04 00:08:39 +01:00
|
|
|
}
|
|
|
|
|
2009-01-25 14:26:53 +01:00
|
|
|
|
2008-11-04 00:08:39 +01:00
|
|
|
QRect scrollButtonsRect;
|
|
|
|
foreach (QObject *child, children()) {
|
|
|
|
QToolButton *childWidget = qobject_cast<QToolButton *>(child);
|
|
|
|
if (childWidget) {
|
|
|
|
if (!childWidget->isVisible()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scrollButtonsRect.isValid()) {
|
|
|
|
scrollButtonsRect = scrollButtonsRect.united(childWidget->geometry());
|
|
|
|
} else {
|
|
|
|
scrollButtonsRect = childWidget->geometry();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scrollButtonsRect.isValid()) {
|
|
|
|
scrollButtonsRect.adjust(2, 4, -2, -4);
|
|
|
|
painter.save();
|
|
|
|
|
|
|
|
QColor background(Plasma::Theme::defaultTheme()->color(Theme::BackgroundColor));
|
|
|
|
background.setAlphaF(0.75);
|
|
|
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
painter.fillPath(PaintUtils::roundedRectangle(scrollButtonsRect, 5), background);
|
|
|
|
painter.restore();
|
|
|
|
|
|
|
|
QStyleOption so;
|
|
|
|
so.initFrom(this);
|
|
|
|
so.palette.setColor(QPalette::ButtonText,
|
|
|
|
Plasma::Theme::defaultTheme()->color(Theme::TextColor));
|
|
|
|
|
|
|
|
so.rect = scrollButtonsRect.adjusted(0, 0, -scrollButtonsRect.width() / 2, 0);
|
|
|
|
style()->drawPrimitive(QStyle::PE_IndicatorArrowLeft, &so, &painter, this);
|
|
|
|
|
|
|
|
so.rect = scrollButtonsRect.adjusted(scrollButtonsRect.width() / 2, 0, 0, 0);
|
|
|
|
style()->drawPrimitive(QStyle::PE_IndicatorArrowRight, &so, &painter, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::resizeEvent(QResizeEvent *event)
|
|
|
|
{
|
2008-11-07 19:03:16 +01:00
|
|
|
KTabBar::resizeEvent(event);
|
2008-11-04 00:08:39 +01:00
|
|
|
d->currentAnimRect = tabRect(currentIndex());
|
|
|
|
d->backgroundSvg->resizeFrame(size());
|
|
|
|
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::tabInserted(int index)
|
|
|
|
{
|
2008-11-07 19:03:16 +01:00
|
|
|
KTabBar::tabInserted(index);
|
2008-11-04 00:08:39 +01:00
|
|
|
emit sizeHintChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::tabRemoved(int index)
|
|
|
|
{
|
2008-11-07 19:03:16 +01:00
|
|
|
KTabBar::tabRemoved(index);
|
2008-11-04 00:08:39 +01:00
|
|
|
emit sizeHintChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::tabLayoutChange()
|
|
|
|
{
|
2008-11-07 19:03:16 +01:00
|
|
|
KTabBar::tabLayoutChange();
|
2008-11-04 00:08:39 +01:00
|
|
|
|
|
|
|
if (shape() != d->shape) {
|
|
|
|
d->shape = shape();
|
|
|
|
emit shapeChanged(d->shape);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::startAnimation()
|
|
|
|
{
|
|
|
|
d->storeLastIndex();
|
|
|
|
Plasma::Animator::self()->customAnimation(
|
|
|
|
10, 150, Plasma::Animator::EaseInOutCurve, this, "onValueChanged");
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::onValueChanged(qreal value)
|
|
|
|
{
|
|
|
|
if ((d->animProgress = value) == 1.0) {
|
|
|
|
animationFinished();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// animation rect
|
|
|
|
QRect rect = tabRect(currentIndex());
|
|
|
|
QRect lastRect = tabRect(lastIndex());
|
|
|
|
int x = isHorizontal() ? (int)(lastRect.x() - value * (lastRect.x() - rect.x())) : rect.x();
|
|
|
|
int y = isHorizontal() ? rect.y() : (int)(lastRect.y() - value * (lastRect.y() - rect.y()));
|
|
|
|
QSizeF sz = lastRect.size() - value * (lastRect.size() - rect.size());
|
|
|
|
d->currentAnimRect = QRect(x, y, (int)(sz.width()), (int)(sz.height()));
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void NativeTabBar::animationFinished()
|
|
|
|
{
|
|
|
|
d->currentAnimRect = QRect();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NativeTabBar::isVertical() const
|
|
|
|
{
|
|
|
|
Shape s = shape();
|
|
|
|
if(s == RoundedWest ||
|
|
|
|
s == RoundedEast ||
|
|
|
|
s == TriangularWest ||
|
|
|
|
s == TriangularEast) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NativeTabBar::isHorizontal() const
|
|
|
|
{
|
|
|
|
return !isVertical();
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize NativeTabBar::tabSize(int index) const
|
|
|
|
{
|
|
|
|
QSize hint;
|
|
|
|
const QFontMetrics metrics(QApplication::font());
|
|
|
|
const QSize textSize = metrics.size(Qt::TextHideMnemonic, tabText(index));
|
|
|
|
hint.rwidth() = textSize.width() + iconSize().width();
|
|
|
|
hint.rheight() = qMax(iconSize().height(), textSize.height());
|
|
|
|
hint.rwidth() += d->buttonLeft + d->buttonRight;
|
|
|
|
hint.rheight() += d->buttonTop + d->buttonBottom;
|
2008-11-18 16:27:04 +01:00
|
|
|
|
|
|
|
if (isVertical()) {
|
2008-12-30 17:41:15 +01:00
|
|
|
hint.rwidth() = qMax(hint.width(), int(minimumWidth() - d->left - d->right));
|
2008-11-18 16:27:04 +01:00
|
|
|
} else {
|
2008-12-30 17:41:15 +01:00
|
|
|
hint.rheight() = qMax(hint.height(), int(minimumHeight() - d->top - d->bottom));
|
2008-11-18 16:27:04 +01:00
|
|
|
}
|
|
|
|
|
2008-11-04 00:08:39 +01:00
|
|
|
return hint;
|
|
|
|
}
|
|
|
|
|
2009-01-25 14:26:53 +01:00
|
|
|
//Unfortunately copied from KTabBar
|
|
|
|
QPoint NativeTabBar::closeButtonPos( int tabIndex ) const
|
|
|
|
{
|
|
|
|
QPoint buttonPos;
|
|
|
|
if ( tabIndex < 0 ) {
|
|
|
|
return buttonPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
int availableHeight = height();
|
|
|
|
if ( tabIndex == currentIndex() ) {
|
|
|
|
QStyleOption option;
|
|
|
|
option.initFrom(this);
|
|
|
|
availableHeight -= style()->pixelMetric( QStyle::PM_TabBarTabShiftVertical, &option, this );
|
|
|
|
}
|
|
|
|
|
|
|
|
const QRect tabBounds = tabRect( tabIndex );
|
|
|
|
const int xInc = (height() - KIconLoader::SizeSmall) / 2;
|
|
|
|
|
|
|
|
if ( layoutDirection() == Qt::RightToLeft ) {
|
|
|
|
buttonPos = tabBounds.topLeft();
|
|
|
|
buttonPos.rx() += xInc;
|
|
|
|
} else {
|
|
|
|
buttonPos = tabBounds.topRight();
|
|
|
|
buttonPos.rx() -= KIconLoader::SizeSmall + xInc;
|
|
|
|
}
|
|
|
|
buttonPos.ry() += (availableHeight - KIconLoader::SizeSmall) / 2;
|
|
|
|
|
|
|
|
return buttonPos;
|
|
|
|
}
|
|
|
|
|
2009-02-13 12:43:58 +01:00
|
|
|
void NativeTabBar::wheelEvent(QWheelEvent *event)
|
|
|
|
{
|
|
|
|
if (underMouse()) {
|
|
|
|
//Cycle tabs with the circular array tecnique
|
|
|
|
if (event->delta() < 0) {
|
|
|
|
int index = currentIndex();
|
|
|
|
//search for an enabled tab
|
|
|
|
for (int i = 0; i < count()-1; ++i) {
|
|
|
|
index = (index + 1) % count();
|
|
|
|
if (isTabEnabled(index)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setCurrentIndex(index);
|
|
|
|
} else {
|
|
|
|
int index = currentIndex();
|
|
|
|
for (int i = 0; i < count()-1; ++i) {
|
|
|
|
index = (count() + index -1) % count();
|
|
|
|
if (isTabEnabled(index)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setCurrentIndex(index);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
QTabBar::wheelEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-04 00:08:39 +01:00
|
|
|
} // namespace Plasma
|
|
|
|
|
|
|
|
#include "nativetabbar_p.moc"
|
|
|
|
|