not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / plasma / applets / kickoff / ui / tabbar.cpp
blob24f861346fcc7c325c5ccd317678c42c210cf00a
1 /*
2 Copyright 2007 Robert Knight <robertknight@gmail.com>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 // Own
21 #include "ui/tabbar.h"
23 // KDE
24 #include <KGlobalSettings>
25 #include <KColorScheme>
27 // Qt
28 #include <QIcon>
29 #include <QMouseEvent>
30 #include <QPainter>
32 #include <QGradient>
33 #include <QLinearGradient>
35 #include <Plasma/Plasma>
36 #include <Plasma/Animator>
37 #include <Plasma/Theme>
38 #include <Plasma/FrameSvg>
40 using namespace Kickoff;
42 TabBar::TabBar(QWidget *parent)
43 : QTabBar(parent),
44 m_hoveredTabIndex(-1),
45 m_switchOnHover(true),
46 m_animateSwitch(true),
47 m_animProgress(1.0)
49 m_lastIndex[0] = -1;
50 connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
52 m_tabSwitchTimer.setSingleShot(true);
53 connect(&m_tabSwitchTimer, SIGNAL(timeout()), this, SLOT(switchToHoveredTab()));
54 setMouseTracking(true);
55 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
56 setUsesScrollButtons(false);
58 background = new Plasma::FrameSvg(this);
59 background->setImagePath("dialogs/kickoff");
60 background->setEnabledBorders(
61 Plasma::FrameSvg::BottomBorder |
62 Plasma::FrameSvg::LeftBorder |
63 Plasma::FrameSvg::RightBorder
65 background->resizeFrame(size());
66 background->setElementPrefix("plain");
68 connect(background, SIGNAL(repaintNeeded()), this, SLOT(update()));
71 void TabBar::setShape(Shape shape)
73 resize(0, 0); // This is required, so that the custom implementation of tabSizeHint,
74 // which expands the tabs to the full width of the widget does not pick up
75 // the previous width, e.g. if the panel is moved from the bottom to the left
76 QTabBar::setShape(shape);
77 resize(sizeHint());
80 void TabBar::setCurrentIndexWithoutAnimation(int index)
82 disconnect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
83 setCurrentIndex(index);
84 storeLastIndex();
85 connect(this, SIGNAL(currentChanged(int)), this, SLOT(startAnimation()));
86 animationFinished();
89 void TabBar::setSwitchTabsOnHover(bool switchOnHover)
91 m_switchOnHover = switchOnHover;
94 bool TabBar::switchTabsOnHover() const
96 return m_switchOnHover;
99 void TabBar::setAnimateSwitch(bool animateSwitch)
101 m_animateSwitch = animateSwitch;
104 bool TabBar::animateSwitch()
106 return m_animateSwitch;
109 QSize TabBar::tabSize(int index) const
111 QSize hint;
112 const QFontMetrics metrics(KGlobalSettings::smallestReadableFont());
113 const QSize textSize = metrics.size(Qt::TextHideMnemonic, tabText(index));
114 hint.rwidth() = qMax(iconSize().width(), textSize.width());
115 hint.rheight() = iconSize().height() + textSize.height();
116 hint.rwidth() += 4 * TAB_CONTENTS_MARGIN;
117 hint.rheight() += 2 * TAB_CONTENTS_MARGIN;
118 return hint;
121 void TabBar::storeLastIndex()
123 // if first run
124 if (m_lastIndex[0] == -1) {
125 m_lastIndex[1] = currentIndex();
127 m_lastIndex[0] = m_lastIndex[1];
128 m_lastIndex[1] = currentIndex();
131 int TabBar::lastIndex() const
133 return m_lastIndex[0];
136 QSize TabBar::tabSizeHint(int index) const
138 QSize hint = tabSize(index);
139 int minwidth = 0;
140 int minheight = 0;
142 Shape s = shape();
143 switch (s) {
144 case RoundedSouth:
145 case TriangularSouth:
146 case RoundedNorth:
147 case TriangularNorth:
148 if (count() > 0) {
149 for (int i = count() - 1; i >= 0; i--) {
150 minwidth += tabSize(i).width();
152 if (minwidth < width()) {
153 hint.rwidth() += (width() - minwidth) / count();
156 break;
157 case RoundedWest:
158 case TriangularWest:
159 case RoundedEast:
160 case TriangularEast:
161 if (count() > 0) {
162 for (int i = count() - 1; i >= 0; i--) {
163 minheight += tabSize(i).height();
165 if (minheight < height()) {
166 hint.rheight() += (height() - minheight) / count();
169 hint.rwidth() = qMax(hint.width(), width());
170 break;
172 return hint;
175 QSize TabBar::sizeHint() const
177 int width = 0;
178 int height = 0;
180 if (isVertical()) {
181 for (int i = count() - 1; i >= 0; i--) {
182 height += tabSize(i).height();
185 width = tabSize(0).width();
186 } else {
187 for (int i = count() - 1; i >= 0; i--) {
188 width += tabSize(i).width();
191 height = tabSize(0).height();
193 return QSize(width, height);
196 QPainterPath TabBar::tabPath(const QRect &_r)
198 const int radius = 6;
199 Shape s = shape();
200 QPainterPath path;
201 QRect r = _r;
203 switch (s) {
204 case RoundedSouth:
205 case TriangularSouth:
206 r.adjust(0, 0, 0, -3);
207 path.moveTo(r.topLeft());
208 // Top left corner
209 path.quadTo(r.topLeft() + QPoint(radius, 0), r.topLeft() + QPoint(radius, radius));
210 path.lineTo(r.bottomLeft() + QPoint(radius, -radius));
211 // Bottom left corner
212 path.quadTo(r.bottomLeft() + QPoint(radius, 0), r.bottomLeft() + QPoint(radius * 2, 0));
213 path.lineTo(r.bottomRight() + QPoint(-radius * 2, 0));
214 // Bottom right corner
215 path.quadTo(r.bottomRight() + QPoint(-radius, 0), r.bottomRight() + QPoint(-radius, -radius));
216 path.lineTo(r.topRight() + QPoint(-radius, radius));
217 // Top right corner
218 path.quadTo(r.topRight() + QPoint(-radius, 0), r.topRight());
219 break;
220 case RoundedNorth:
221 case TriangularNorth:
222 r.adjust(0, 3, 0, 1);
223 path.moveTo(r.bottomLeft());
224 // Bottom left corner
225 path.quadTo(r.bottomLeft() + QPoint(radius, 0), r.bottomLeft() + QPoint(radius, -radius));
226 // Top left corner
227 path.lineTo(r.topLeft() + QPoint(radius, radius));
228 path.quadTo(r.topLeft() + QPoint(radius, 0), r.topLeft() + QPoint(radius * 2, 0));
229 // Top right corner
230 path.lineTo(r.topRight() + QPoint(-radius * 2, 0));
231 path.quadTo(r.topRight() + QPoint(-radius, 0), r.topRight() + QPoint(-radius, radius));
232 // Bottom right corner
233 path.lineTo(r.bottomRight() + QPoint(-radius, -radius));
234 path.quadTo(r.bottomRight() + QPoint(-radius, 0), r.bottomRight());
235 break;
236 case RoundedWest:
237 case TriangularWest:
238 r.adjust(3, 0, 1, 0);
239 path.moveTo(r.topRight());
240 // Top right corner
241 path.lineTo(r.topRight());
242 path.quadTo(r.topRight() + QPoint(0, radius), r.topRight() + QPoint(-radius, radius));
243 // Top left corner
244 path.lineTo(r.topLeft() + QPoint(radius, radius));
245 path.quadTo(r.topLeft() + QPoint(0, radius), r.topLeft() + QPoint(0, radius * 2));
246 // Bottom left corner
247 path.lineTo(r.bottomLeft() + QPoint(0, -radius * 2));
248 path.quadTo(r.bottomLeft() + QPoint(0, -radius), r.bottomLeft() + QPoint(radius, -radius));
249 // Bottom right corner
250 path.lineTo(r.bottomRight() + QPoint(-radius, -radius));
251 path.quadTo(r.bottomRight() + QPoint(0, -radius), r.bottomRight());
252 break;
253 case RoundedEast:
254 case TriangularEast:
255 r.adjust(0, 0, -3, 0);
256 path.moveTo(r.topLeft());
257 // Top left corner
258 path.quadTo(r.topLeft() + QPoint(0, radius), r.topLeft() + QPoint(radius, radius));
259 // Top right corner
260 path.lineTo(r.topRight() + QPoint(-radius, radius));
261 path.quadTo(r.topRight() + QPoint(0, radius), r.topRight() + QPoint(0, radius * 2));
262 // Bottom right corner
263 path.lineTo(r.bottomRight() + QPoint(0, -radius * 2));
264 path.quadTo(r.bottomRight() + QPoint(0, -radius), r.bottomRight() + QPoint(-radius, -radius));
265 // Bottom left corner
266 path.lineTo(r.bottomLeft() + QPoint(radius, -radius));
267 path.quadTo(r.bottomLeft() + QPoint(0, -radius), r.bottomLeft());
268 break;
271 return path;
274 void TabBar::paintEvent(QPaintEvent *event)
276 Q_UNUSED(event)
277 QPainter painter(this);
278 //int numTabs = count();
279 int currentTab = currentIndex();
281 background->paintFrame(&painter);
283 //bool ltr = painter.layoutDirection() == Qt::LeftToRight; // Not yet used
284 painter.setFont(KGlobalSettings::smallestReadableFont());
286 // Drawing Tabborders
287 QRect movingRect;
289 if (m_currentAnimRect.isNull()) {
290 movingRect = tabRect(currentIndex());
291 } else {
292 movingRect = m_currentAnimRect;
294 QPainterPath path = tabPath(movingRect);
296 painter.save();
297 painter.setPen(QPen(palette().base(), 1));
299 painter.setRenderHint(QPainter::Antialiasing);
300 painter.fillPath(path, palette().base());
302 painter.restore();
304 QFontMetrics metrics(painter.font());
305 int textHeight = metrics.height();
307 for (int i = 0; i < count(); i++) {
308 QRect rect = tabRect(i).adjusted(TAB_CONTENTS_MARGIN, TAB_CONTENTS_MARGIN,
309 -TAB_CONTENTS_MARGIN, -TAB_CONTENTS_MARGIN);
310 // draw tab icon
311 QRect iconRect = rect;
312 iconRect.setBottom(iconRect.bottom() - textHeight);
313 iconRect.adjust(0, (isVertical() ? 1 : 0) * TAB_CONTENTS_MARGIN + 3, 0, 0);
314 tabIcon(i).paint(&painter, iconRect);
316 // draw tab text
317 if (i != currentTab || m_animProgress < 0.9) {
318 //painter.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText), 0));
319 painter.setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
320 } else {
321 painter.setPen(QPen(KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText), 0));
323 QRect textRect = rect;
324 textRect.setTop(textRect.bottom() - textHeight);
325 painter.drawText(textRect, Qt::AlignCenter | Qt::TextHideMnemonic, tabText(i));
329 void TabBar::leaveEvent(QEvent *event)
331 Q_UNUSED(event)
332 m_hoveredTabIndex = -1;
335 void TabBar::mouseMoveEvent(QMouseEvent *event)
337 m_hoveredTabIndex = tabAt(event->pos());
338 if (m_switchOnHover && m_hoveredTabIndex > -1 && m_hoveredTabIndex != currentIndex()) {
339 m_tabSwitchTimer.stop();
340 m_tabSwitchTimer.start(50);
344 void TabBar::resizeEvent(QResizeEvent* event)
346 QTabBar::resizeEvent(event);
347 m_currentAnimRect = tabRect(currentIndex());
349 background->resizeFrame(event->size());
351 update();
354 void TabBar::switchToHoveredTab()
356 if (m_hoveredTabIndex < 0 || m_hoveredTabIndex == currentIndex()) {
357 return;
360 if (m_animateSwitch) {
361 setCurrentIndex(m_hoveredTabIndex);
362 } else {
363 setCurrentIndexWithoutAnimation(m_hoveredTabIndex);
367 void TabBar::startAnimation()
369 storeLastIndex();
370 Plasma::Animator::self()->customAnimation(10, 150, Plasma::Animator::EaseInOutCurve, this, "onValueChanged");
374 void TabBar::onValueChanged(qreal value)
376 if ((m_animProgress = value) == 1.0) {
377 animationFinished();
378 return;
381 // animation rect
382 QRect rect = tabRect(currentIndex());
383 QRect lastRect = tabRect(lastIndex());
384 int x = isHorizontal() ? (int)(lastRect.x() - value * (lastRect.x() - rect.x())) : rect.x();
385 int y = isHorizontal() ? rect.y() : (int)(lastRect.y() - value * (lastRect.y() - rect.y()));
386 QSizeF sz = lastRect.size() - value * (lastRect.size() - rect.size());
387 m_currentAnimRect = QRect(x, y, (int)(sz.width()), (int)(sz.height()));
388 update();
391 void TabBar::animationFinished()
393 m_currentAnimRect = QRect();
394 update();
397 bool TabBar::isVertical() const
399 Shape s = shape();
400 if (s == RoundedWest ||
401 s == RoundedEast ||
402 s == TriangularWest ||
403 s == TriangularEast) {
404 return true;
406 return false;
409 bool TabBar::isHorizontal() const
411 return !isVertical();
414 #include "tabbar.moc"