fancytabwidget.cpp 14.4 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
5
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "fancytabwidget.h"
31
#include <utils/stylehelper.h>
con's avatar
con committed
32
#include <utils/styledbar.h>
con's avatar
con committed
33
34
35
36
37
38
39
40
41
42
43
44
45

#include <QDebug>

#include <QtGui/QColorDialog>
#include <QtGui/QHBoxLayout>
#include <QtGui/QVBoxLayout>
#include <QtGui/QMouseEvent>
#include <QtGui/QWindowsStyle>
#include <QtGui/QPainter>
#include <QtGui/QSplitter>
#include <QtGui/QStackedLayout>
#include <QtGui/QStatusBar>
#include <QtGui/QToolButton>
46
#include <QtGui/QToolTip>
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
47
48
#include <QtCore/QAnimationGroup>
#include <QtCore/QPropertyAnimation>
con's avatar
con committed
49
50
51
52
53
54
55

using namespace Core;
using namespace Internal;

const int FancyTabBar::m_rounding = 22;
const int FancyTabBar::m_textPadding = 4;

56
57
void FancyTab::fadeIn()
{
58
59
60
61
    animator.stop();
    animator.setDuration(80);
    animator.setEndValue(40);
    animator.start();
62
63
64
65
}

void FancyTab::fadeOut()
{
66
67
68
69
    animator.stop();
    animator.setDuration(160);
    animator.setEndValue(0);
    animator.start();
70
71
}

72
73
74
75
76
77
void FancyTab::setFader(float value)
{
    m_fader = value;
    tabbar->update();
}

con's avatar
con committed
78
FancyTabBar::FancyTabBar(QWidget *parent)
79
    : QWidget(parent)
con's avatar
con committed
80
{
81
    m_hoverIndex = -1;
con's avatar
con committed
82
    m_currentIndex = -1;
83
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
con's avatar
con committed
84
85
86
87
88
    setStyle(new QWindowsStyle);
    setMinimumWidth(qMax(2 * m_rounding, 40));
    setAttribute(Qt::WA_Hover, true);
    setFocusPolicy(Qt::NoFocus);
    setMouseTracking(true); // Needed for hover events
89
90
91
92
    m_triggerTimer.setSingleShot(true);

    // We use a zerotimer to keep the sidebar responsive
    connect(&m_triggerTimer, SIGNAL(timeout()), this, SLOT(emitCurrentIndex()));
con's avatar
con committed
93
94
95
96
97
98
99
}

FancyTabBar::~FancyTabBar()
{
    delete style();
}

100
QSize FancyTabBar::tabSizeHint(bool minimum) const
con's avatar
con committed
101
102
{
    QFont boldFont(font());
103
    boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
con's avatar
con committed
104
105
    boldFont.setBold(true);
    QFontMetrics fm(boldFont);
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
106
    int spacing = 8;
con's avatar
con committed
107
    int width = 60 + spacing + 2;
108

mae's avatar
mae committed
109
    int iconHeight = minimum ? 0 : 32;
110
    return QSize(width, iconHeight + spacing + fm.height());
con's avatar
con committed
111
112
113
114
115
116
117
118
119
120
121
122
}

void FancyTabBar::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    QPainter p(this);

    for (int i = 0; i < count(); ++i)
        if (i != currentIndex())
            paintTab(&p, i);

    // paint active tab last, since it overlaps the neighbors
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
123
124
    if (currentIndex() != -1)
        paintTab(&p, currentIndex());
con's avatar
con committed
125
126
127
128
129
}

// Handle hover events for mouse fade ins
void FancyTabBar::mouseMoveEvent(QMouseEvent *e)
{
130
131
132
133
134
135
    int newHover = -1;
    for (int i = 0; i < count(); ++i) {
        QRect area = tabRect(i);
        if (area.contains(e->pos())) {
            newHover = i;
            break;
con's avatar
con committed
136
        }
137
138
139
    }
    if (newHover == m_hoverIndex)
        return;
mae's avatar
mae committed
140

141
142
    if (validIndex(m_hoverIndex))
        m_tabs[m_hoverIndex]->fadeOut();
143

144
    m_hoverIndex = newHover;
mae's avatar
mae committed
145

146
147
148
    if (validIndex(m_hoverIndex)) {
        m_tabs[m_hoverIndex]->fadeIn();
        m_hoverRect = tabRect(m_hoverIndex);
con's avatar
con committed
149
150
151
    }
}

152
153
154
bool FancyTabBar::event(QEvent *event)
{
    if (event->type() == QEvent::ToolTip) {
155
        if (validIndex(m_hoverIndex)) {
156
157
158
159
160
161
162
163
164
165
            QString tt = tabToolTip(m_hoverIndex);
            if (!tt.isEmpty()) {
                QToolTip::showText(static_cast<QHelpEvent*>(event)->globalPos(), tt, this);
                return true;
            }
        }
    }
    return QWidget::event(event);
}

con's avatar
con committed
166
167
168
// Resets hover animation on mouse enter
void FancyTabBar::enterEvent(QEvent *e)
{
169
    Q_UNUSED(e)
con's avatar
con committed
170
    m_hoverRect = QRect();
171
    m_hoverIndex = -1;
con's avatar
con committed
172
173
174
175
176
}

// Resets hover animation on mouse enter
void FancyTabBar::leaveEvent(QEvent *e)
{
177
    Q_UNUSED(e)
178
179
180
181
    m_hoverIndex = -1;
    m_hoverRect = QRect();
    for (int i = 0 ; i < m_tabs.count() ; ++i) {
        m_tabs[i]->fadeOut();
182
183
    }
}
con's avatar
con committed
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
QSize FancyTabBar::sizeHint() const
{
    QSize sh = tabSizeHint();
    return QSize(sh.width(), sh.height() * m_tabs.count());
}

QSize FancyTabBar::minimumSizeHint() const
{
    QSize sh = tabSizeHint(true);
    return QSize(sh.width(), sh.height() * m_tabs.count());
}

QRect FancyTabBar::tabRect(int index) const
{
    QSize sh = tabSizeHint();

    if (sh.height() * m_tabs.count() > height())
        sh.setHeight(height() / m_tabs.count());

    return QRect(0, index * sh.height(), sh.width(), sh.height());

}

208
209
210
211
212
213
214
215
// This keeps the sidebar responsive since
// we get a repaint before loading the
// mode itself
void FancyTabBar::emitCurrentIndex()
{
    emit currentChanged(m_currentIndex);
}

216
217
218
219
220
221
222
223
224
void FancyTabBar::mousePressEvent(QMouseEvent *e)
{
    e->accept();
    for (int i = 0; i < m_tabs.count(); ++i) {
        if (tabRect(i).contains(e->pos())) {
            setCurrentIndex(i);
            break;
        }
    }
con's avatar
con committed
225
226
227
228
}

void FancyTabBar::paintTab(QPainter *painter, int tabIndex) const
{
229
230
231
232
    if (!validIndex(tabIndex)) {
        qWarning("invalid index");
        return;
    }
con's avatar
con committed
233
234
    painter->save();

235
236
    QRect rect = tabRect(tabIndex);
    bool selected = (tabIndex == m_currentIndex);
237
    bool enabled = isTabEnabled(tabIndex);
con's avatar
con committed
238
239

    if (selected) {
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
240
        //background
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
241
242
243
244
245
246
247
248
249
250
        painter->save();
        QLinearGradient grad(rect.topLeft(), rect.topRight());
        grad.setColorAt(0, QColor(255, 255, 255, 160));
        grad.setColorAt(1, QColor(255, 255, 255, 210));
        painter->fillRect(rect.adjusted(0, 0, 0, -1), grad);
        painter->restore();

        //shadows
        painter->setPen(QColor(0, 0, 0, 110));
        painter->drawLine(rect.topLeft() + QPoint(1,-1), rect.topRight() - QPoint(0,1));
con's avatar
con committed
251
        painter->drawLine(rect.bottomLeft(), rect.bottomRight());
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
252
253
254
255
        painter->setPen(QColor(0, 0, 0, 40));
        painter->drawLine(rect.topLeft(), rect.bottomLeft());

        //highlights
256
257
258
259
260
        painter->setPen(QColor(255, 255, 255, 50));
        painter->drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0,2));
        painter->drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(0,1));
        painter->setPen(QColor(255, 255, 255, 40));
        painter->drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight());
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
261
        painter->drawLine(rect.topRight() + QPoint(0, 1), rect.bottomRight() - QPoint(0, 1));
262
        painter->drawLine(rect.bottomLeft() + QPoint(0,-1), rect.bottomRight()-QPoint(0,1));
con's avatar
con committed
263
264
    }

265
266
267
    QString tabText(this->tabText(tabIndex));
    QRect tabTextRect(tabRect(tabIndex));
    QRect tabIconRect(tabTextRect);
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
268
    tabTextRect.translate(0, -2);
con's avatar
con committed
269
    QFont boldFont(painter->font());
270
    boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize());
con's avatar
con committed
271
272
    boldFont.setBold(true);
    painter->setFont(boldFont);
273
    painter->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110));
con's avatar
con committed
274
    int textFlags = Qt::AlignCenter | Qt::AlignBottom | Qt::ElideRight | Qt::TextWordWrap;
275
276
277
278
279
280
    if (enabled) {
        painter->drawText(tabTextRect, textFlags, tabText);
        painter->setPen(selected ? QColor(60, 60, 60) : Utils::StyleHelper::panelTextColor());
    } else {
        painter->setPen(selected ? Utils::StyleHelper::panelTextColor() : QColor(255, 255, 255, 120));
    }
281
#ifndef Q_WS_MAC
282
283
284
285
286
287
288
289
290
291
292
293
294
    if (!selected && enabled) {
        painter->save();
        int fader = int(m_tabs[tabIndex]->fader());
        QLinearGradient grad(rect.topLeft(), rect.topRight());
        grad.setColorAt(0, Qt::transparent);
        grad.setColorAt(0.5, QColor(255, 255, 255, fader));
        grad.setColorAt(1, Qt::transparent);
        painter->fillRect(rect, grad);
        painter->setPen(QPen(grad, 1.0));
        painter->drawLine(rect.topLeft(), rect.topRight());
        painter->drawLine(rect.bottomLeft(), rect.bottomRight());
        painter->restore();
    }
295
#endif
296
297
298
299

    if (!enabled)
        painter->setOpacity(0.7);

con's avatar
con committed
300
301
    int textHeight = painter->fontMetrics().boundingRect(QRect(0, 0, width(), height()), Qt::TextWordWrap, tabText).height();
    tabIconRect.adjust(0, 4, 0, -textHeight);
302
303
304
    int iconSize = qMin(tabIconRect.width(), tabIconRect.height());
    if (iconSize > 4)
        style()->drawItemPixmap(painter, tabIconRect, Qt::AlignCenter | Qt::AlignVCenter,
305
                                tabIcon(tabIndex).pixmap(tabIconRect.size(), enabled ? QIcon::Normal : QIcon::Disabled));
306

con's avatar
con committed
307
308
309
310
311
    painter->translate(0, -1);
    painter->drawText(tabTextRect, textFlags, tabText);
    painter->restore();
}

312
void FancyTabBar::setCurrentIndex(int index) {
313
314
315
    if (isTabEnabled(index)) {
        m_currentIndex = index;
        update();
316
        m_triggerTimer.start(0);
317
    }
con's avatar
con committed
318
319
}

320
321
322
323
324
325
void FancyTabBar::setTabEnabled(int index, bool enable)
{
    Q_ASSERT(index < m_tabs.size());
    Q_ASSERT(index >= 0);

    if (index < m_tabs.size() && index >= 0) {
326
        m_tabs[index]->enabled = enable;
327
328
329
330
331
332
333
334
335
336
        update(tabRect(index));
    }
}

bool FancyTabBar::isTabEnabled(int index) const
{
    Q_ASSERT(index < m_tabs.size());
    Q_ASSERT(index >= 0);

    if (index < m_tabs.size() && index >= 0)
337
        return m_tabs[index]->enabled;
338
339
340
341
342

    return false;
}


con's avatar
con committed
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
//////
// FancyColorButton
//////

class FancyColorButton : public QWidget
{
public:
    FancyColorButton(QWidget *parent)
      : m_parent(parent)
    {
        setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
    }

    void mousePressEvent(QMouseEvent *ev)
    {
358
        if (ev->modifiers() & Qt::ShiftModifier)
359
            Utils::StyleHelper::setBaseColor(QColorDialog::getColor(Utils::StyleHelper::requestedBaseColor(), m_parent));
con's avatar
con committed
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
    }
private:
    QWidget *m_parent;
};

//////
// FancyTabWidget
//////

FancyTabWidget::FancyTabWidget(QWidget *parent)
    : QWidget(parent)
{
    m_tabBar = new FancyTabBar(this);

    m_selectionWidget = new QWidget(this);
    QVBoxLayout *selectionLayout = new QVBoxLayout;
    selectionLayout->setSpacing(0);
    selectionLayout->setMargin(0);

379
    Utils::StyledBar *bar = new Utils::StyledBar;
con's avatar
con committed
380
381
382
383
    QHBoxLayout *layout = new QHBoxLayout(bar);
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(new FancyColorButton(this));
con's avatar
con committed
384
385
    selectionLayout->addWidget(bar);

mae's avatar
mae committed
386
    selectionLayout->addWidget(m_tabBar, 1);
con's avatar
con committed
387
    m_selectionWidget->setLayout(selectionLayout);
388
    m_selectionWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
con's avatar
con committed
389
390

    m_cornerWidgetContainer = new QWidget(this);
mae's avatar
mae committed
391
    m_cornerWidgetContainer->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
con's avatar
con committed
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
    m_cornerWidgetContainer->setAutoFillBackground(false);

    QVBoxLayout *cornerWidgetLayout = new QVBoxLayout;
    cornerWidgetLayout->setSpacing(0);
    cornerWidgetLayout->setMargin(0);
    cornerWidgetLayout->addStretch();
    m_cornerWidgetContainer->setLayout(cornerWidgetLayout);

    selectionLayout->addWidget(m_cornerWidgetContainer, 0);

    m_modesStack = new QStackedLayout;
    m_statusBar = new QStatusBar;
    m_statusBar->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);

    QVBoxLayout *vlayout = new QVBoxLayout;
    vlayout->setMargin(0);
    vlayout->setSpacing(0);
    vlayout->addLayout(m_modesStack);
    vlayout->addWidget(m_statusBar);

con's avatar
con committed
412
413
414
415
416
417
    QHBoxLayout *mainLayout = new QHBoxLayout;
    mainLayout->setMargin(0);
    mainLayout->setSpacing(1);
    mainLayout->addWidget(m_selectionWidget);
    mainLayout->addLayout(vlayout);
    setLayout(mainLayout);
con's avatar
con committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446

    connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(showWidget(int)));
}

void FancyTabWidget::insertTab(int index, QWidget *tab, const QIcon &icon, const QString &label)
{
    m_modesStack->insertWidget(index, tab);
    m_tabBar->insertTab(index, icon, label);
}

void FancyTabWidget::removeTab(int index)
{
    m_modesStack->removeWidget(m_modesStack->widget(index));
    m_tabBar->removeTab(index);
}

void FancyTabWidget::setBackgroundBrush(const QBrush &brush)
{
    QPalette pal = m_tabBar->palette();
    pal.setBrush(QPalette::Mid, brush);
    m_tabBar->setPalette(pal);
    pal = m_cornerWidgetContainer->palette();
    pal.setBrush(QPalette::Mid, brush);
    m_cornerWidgetContainer->setPalette(pal);
}

void FancyTabWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
447
    QPainter painter(this);
con's avatar
con committed
448
449

    QRect rect = m_selectionWidget->rect().adjusted(0, 0, 1, 0);
450
    rect = style()->visualRect(layoutDirection(), geometry(), rect);
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
451
452
453
454
    Utils::StyleHelper::verticalGradient(&painter, rect, rect);
    painter.setPen(Utils::StyleHelper::borderColor());
    painter.drawLine(rect.topRight(), rect.bottomRight());

Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
455
    QColor light = Utils::StyleHelper::sidebarHighlight();
Jens Bache-Wiig's avatar
Jens Bache-Wiig committed
456
457
    painter.setPen(light);
    painter.drawLine(rect.bottomLeft(), rect.bottomRight());
con's avatar
con committed
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
}

void FancyTabWidget::insertCornerWidget(int pos, QWidget *widget)
{
    QVBoxLayout *layout = static_cast<QVBoxLayout *>(m_cornerWidgetContainer->layout());
    layout->insertWidget(pos, widget);
}

int FancyTabWidget::cornerWidgetCount() const
{
    return m_cornerWidgetContainer->layout()->count();
}

void FancyTabWidget::addCornerWidget(QWidget *widget)
{
    m_cornerWidgetContainer->layout()->addWidget(widget);
}

int FancyTabWidget::currentIndex() const
{
    return m_tabBar->currentIndex();
}

QStatusBar *FancyTabWidget::statusBar() const
{
    return m_statusBar;
}

void FancyTabWidget::setCurrentIndex(int index)
{
488
489
    if (m_tabBar->isTabEnabled(index))
        m_tabBar->setCurrentIndex(index);
con's avatar
con committed
490
491
492
493
494
495
496
497
498
499
500
501
502
}

void FancyTabWidget::showWidget(int index)
{
    emit currentAboutToShow(index);
    m_modesStack->setCurrentIndex(index);
    emit currentChanged(index);
}

void FancyTabWidget::setTabToolTip(int index, const QString &toolTip)
{
    m_tabBar->setTabToolTip(index, toolTip);
}
503
504
505
506
507
508
509
510
511
512

void FancyTabWidget::setTabEnabled(int index, bool enable)
{
    m_tabBar->setTabEnabled(index, enable);
}

bool FancyTabWidget::isTabEnabled(int index) const
{
    return m_tabBar->isTabEnabled(index);
}