33#include <QAnimationGroup>
34#include <QColorDialog>
39#include <QSignalMapper>
41#include <QStackedLayout>
42#include <QStyleOptionTab>
46#include <QActionGroup>
50using namespace Internal;
52const int FancyTabBar::m_rounding = 22;
53const int FancyTabBar::m_textPadding = 4;
55void FancyTabProxyStyle::drawControl(
56 ControlElement element,
const QStyleOption* option,
57 QPainter* p,
const QWidget* widget)
const
60 const auto* v_opt = qstyleoption_cast<const QStyleOptionTab*>(option);
62 if (element != CE_TabBarTab || !v_opt) {
63 QProxyStyle::drawControl(element, option, p, widget);
67 const QRect rect = v_opt->rect;
68 const bool selected = v_opt->state & State_Selected;
69 const bool vertical_tabs = v_opt->shape == QTabBar::RoundedWest;
70 const QString text = v_opt->text;
75 QLinearGradient grad(rect.topLeft(), rect.topRight());
76 grad.setColorAt(0, QColor(255, 255, 255, 140));
77 grad.setColorAt(1, QColor(255, 255, 255, 210));
78 p->fillRect(rect.adjusted(0, 0, 0, -1), grad);
82 p->setPen(QColor(0, 0, 0, 110));
83 p->drawLine(rect.topLeft() + QPoint(1, -1), rect.topRight() - QPoint(0, 1));
84 p->drawLine(rect.bottomLeft(), rect.bottomRight());
85 p->setPen(QColor(0, 0, 0, 40));
86 p->drawLine(rect.topLeft(), rect.bottomLeft());
89 p->setPen(QColor(255, 255, 255, 50));
90 p->drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0, 2));
91 p->drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(0, 1));
92 p->setPen(QColor(255, 255, 255, 40));
93 p->drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight());
94 p->drawLine(rect.topRight() + QPoint(0, 1), rect.bottomRight() - QPoint(0, 1));
95 p->drawLine(rect.bottomLeft() + QPoint(0, -1), rect.bottomRight() - QPoint(0, 1));
100 m = QTransform::fromTranslate(rect.left(), rect.bottom());
104 m = QTransform::fromTranslate(rect.left(), rect.top());
107 const QRect draw_rect(QPoint(0, 0), m.mapRect(rect).size());
112 QRect icon_rect(QPoint(8, 0), v_opt->iconSize);
113 QRect text_rect(icon_rect.topRight() + QPoint(4, 0), draw_rect.size());
114 text_rect.setRight(draw_rect.width());
115 icon_rect.translate(0, (draw_rect.height() - icon_rect.height()) / 2);
117 QFont boldFont(p->font());
119 boldFont.setBold(
true);
120 p->setFont(boldFont);
121 p->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110));
122 int textFlags = Qt::AlignHCenter | Qt::AlignVCenter;
123 p->drawText(text_rect, textFlags, text);
126 const QString fader_key =
QSL(
"tab_") + text +
QSL(
"_fader");
127 const QString animation_key =
QSL(
"tab_") + text +
QSL(
"_animation");
129 const QString tab_hover = widget->property(
"tab_hover").toString();
130 int fader = widget->property(fader_key.toUtf8().constData()).toInt();
131 auto* animation = widget->property(animation_key.toUtf8().constData()).value<QPropertyAnimation*>();
134 auto* mut_widget =
const_cast<QWidget*
>(widget);
136 mut_widget->setProperty(fader_key.toUtf8().constData(), fader);
137 animation =
new QPropertyAnimation(mut_widget, fader_key.toUtf8(), mut_widget);
138 connect(animation, SIGNAL(valueChanged(QVariant)), mut_widget, SLOT(update()));
139 mut_widget->setProperty(animation_key.toUtf8().constData(), QVariant::fromValue(animation));
142 if (text == tab_hover) {
143 if (animation->state() != QAbstractAnimation::Running && fader != 40) {
145 animation->setDuration(80);
146 animation->setEndValue(40);
151 if (animation->state() != QAbstractAnimation::Running && fader != 0) {
153 animation->setDuration(160);
154 animation->setEndValue(0);
161 p->fillRect(draw_rect, QColor(255, 255, 255, fader));
162 p->setPen(QPen(QColor(255, 255, 255, fader), 1.0));
163 p->drawLine(draw_rect.topLeft(), vertical_tabs ? draw_rect.bottomLeft() : draw_rect.topRight());
164 p->drawLine(draw_rect.bottomRight(), vertical_tabs ? draw_rect.topRight() : draw_rect.bottomLeft());
171 p->drawText(text_rect.translated(0, -1), textFlags, text);
178 if (QString::fromLatin1(widget->metaObject()->className()) == QLatin1String(
"QTabBar")) {
179 widget->setMouseTracking(
true);
180 widget->installEventFilter(
this);
182 QProxyStyle::polish(widget);
187 QProxyStyle::polish(app);
192 QProxyStyle::polish(palette);
197 auto* bar = qobject_cast<QTabBar*>(o);
198 if (bar && (e->type() == QEvent::MouseMove || e->type() == QEvent::Leave)) {
199 auto*
event =
static_cast<QMouseEvent*
>(e);
200 const QString old_hovered_tab = bar->property(
"tab_hover").toString();
201 const QString hovered_tab = e->type() == QEvent::Leave ? QString() : bar->tabText(bar->tabAt(event->pos()));
202 bar->setProperty(
"tab_hover", hovered_tab);
204 if (old_hovered_tab != hovered_tab) {
213 : QWidget(tabbar), tabbar(tabbar), m_fader(0)
215 animator.setPropertyName(
"fader");
216 animator.setTargetObject(
this);
217 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
223 animator.setDuration(80);
224 animator.setEndValue(40);
231 animator.setDuration(160);
232 animator.setEndValue(0);
246 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
248 setMinimumWidth(qMax(2 * m_rounding, 40));
249 setAttribute(Qt::WA_Hover,
true);
250 setFocusPolicy(Qt::NoFocus);
251 setMouseTracking(
true);
252 m_triggerTimer.setSingleShot(
true);
254 auto* layout =
new QVBoxLayout;
255 layout->addSpacerItem(
new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding));
256 layout->setSpacing(0);
257 layout->setContentsMargins(0, 0, 0, 0);
271 QFont boldFont(font());
273 boldFont.setBold(
true);
274 QFontMetrics fm(boldFont);
276 int width = 60 + spacing + 2;
278 QSize ret(width, iconHeight + spacing + fm.height());
282QSize FancyTabBar::tabSizeHint(
bool minimum)
const
284 QFont boldFont(font());
286 boldFont.setBold(
true);
287 QFontMetrics fm(boldFont);
289 int width = 60 + spacing + 2;
290 int iconHeight = minimum ? 0 : 32;
291 return QSize(width, iconHeight + spacing + fm.height());
322 QSize sh = tabSizeHint();
323 return QSize(sh.width(), sh.height() * m_tabs.count());
328 QSize sh = tabSizeHint(
true);
329 return QSize(sh.width(), sh.height() * m_tabs.count());
334 return m_tabs[index]->geometry();
339 return m_tabs[index]->toolTip();
344 m_tabs[index]->setToolTip(toolTip);
358 for (
int index = 0; index < m_tabs.count(); ++index) {
359 if (
tabRect(index).contains(e->pos())) {
360 m_currentIndex = index;
362 m_triggerTimer.start(0);
374 qobject_cast<QVBoxLayout*>(layout())->insertWidget(layout()->
count() - 1, tab);
379 qobject_cast<QVBoxLayout*>(layout())->insertSpacerItem(layout()->
count() - 1,
380 new QSpacerItem(0, size, QSizePolicy::Fixed, QSizePolicy::Maximum));
386 qWarning(
"invalid index");
391 QRect rect =
tabRect(tabIndex);
392 bool selected = (tabIndex == m_currentIndex);
397 QLinearGradient grad(rect.topLeft(), rect.topRight());
398 grad.setColorAt(0, QColor(255, 255, 255, 140));
399 grad.setColorAt(1, QColor(255, 255, 255, 210));
400 painter->fillRect(rect.adjusted(0, 0, 0, -1), grad);
404 painter->setPen(QColor(0, 0, 0, 110));
405 painter->drawLine(rect.topLeft() + QPoint(1, -1), rect.topRight() - QPoint(0, 1));
406 painter->drawLine(rect.bottomLeft(), rect.bottomRight());
407 painter->setPen(QColor(0, 0, 0, 40));
408 painter->drawLine(rect.topLeft(), rect.bottomLeft());
411 painter->setPen(QColor(255, 255, 255, 50));
412 painter->drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0, 2));
413 painter->drawLine(rect.bottomLeft() + QPoint(0, 1), rect.bottomRight() + QPoint(0, 1));
414 painter->setPen(QColor(255, 255, 255, 40));
415 painter->drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight());
416 painter->drawLine(rect.topRight() + QPoint(0, 1), rect.bottomRight() - QPoint(0, 1));
417 painter->drawLine(rect.bottomLeft() + QPoint(0, -1), rect.bottomRight() - QPoint(0, 1));
421 QRect tabTextRect(
tabRect(tabIndex));
422 QRect tabIconRect(tabTextRect);
423 tabIconRect.adjust(+4, +4, -4, -4);
424 tabTextRect.translate(0, -2);
425 QFont boldFont(painter->font());
427 boldFont.setBold(
true);
428 painter->setFont(boldFont);
429 painter->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110));
436 int fader = int(m_tabs[tabIndex]->fader());
437 QLinearGradient grad(rect.topLeft(), rect.topRight());
438 grad.setColorAt(0, Qt::transparent);
439 grad.setColorAt(0.5, QColor(255, 255, 255, fader));
440 grad.setColorAt(1, Qt::transparent);
443 painter->fillRect(rect, QColor(255, 255, 255, fader));
444 painter->setPen(QPen(QColor(255, 255, 255, fader), 1.0));
445 painter->drawLine(rect.topLeft(), rect.topRight());
446 painter->drawLine(rect.bottomLeft(), rect.bottomRight());
452 tabIconRect.adjust(0, 6, 0, -6);
455 painter->translate(0, -1);
462 m_currentIndex = index;
477 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
481 if (ev->modifiers() & Qt::ShiftModifier) {
497 stack_(new QStackedLayout),
498 side_widget_(new QWidget),
499 side_layout_(new QVBoxLayout),
500 top_layout_(new QVBoxLayout),
501 use_background_(false),
505 side_layout_->setSpacing(0);
506 side_layout_->setContentsMargins(0, 0, 0, 0);
507 side_layout_->addSpacerItem(
new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding));
509 side_widget_->setLayout(side_layout_);
510 side_widget_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
512 top_layout_->setContentsMargins(0, 0, 0, 0);
513 top_layout_->setSpacing(0);
514 top_layout_->addLayout(stack_);
516 auto* main_layout =
new QHBoxLayout;
517 main_layout->setContentsMargins(0, 0, 0, 0);
518 main_layout->setSpacing(1);
519 main_layout->addWidget(side_widget_);
520 main_layout->addLayout(top_layout_);
521 setLayout(main_layout);
526 stack_->addWidget(tab);
527 items_ <<
Item(icon, label);
532 items_ <<
Item(size);
537 background_pixmap_ = pixmap;
543 if (!use_background_) {
547 QPainter painter(
this);
549 QRect rect = side_widget_->rect().adjusted(0, 0, 1, 0);
550 rect = style()->visualRect(layoutDirection(), geometry(), rect);
553 if (!background_pixmap_.isNull()) {
554 QRect pixmap_rect(background_pixmap_.rect());
555 pixmap_rect.moveTo(rect.topLeft());
557 while (pixmap_rect.top() < rect.bottom()) {
558 QRect source_rect(pixmap_rect.intersected(rect));
559 source_rect.moveTo(0, 0);
560 painter.drawPixmap(pixmap_rect.topLeft(), background_pixmap_, source_rect);
561 pixmap_rect.moveTop(pixmap_rect.bottom() - 10);
566 painter.drawLine(rect.topRight(), rect.bottomRight());
569 painter.setPen(
light);
570 painter.drawLine(rect.bottomLeft(), rect.bottomRight());
575 return stack_->currentIndex();
580 if (
auto* bar = qobject_cast<FancyTabBar*>(tab_bar_)) {
581 bar->setCurrentIndex(index);
583 else if (
auto* bar = qobject_cast<QTabBar*>(tab_bar_)) {
584 bar->setCurrentIndex(index);
587 stack_->setCurrentIndex(index);
591void FancyTabWidget::ShowWidget(
int index)
593 stack_->setCurrentIndex(index);
599 top_layout_->addWidget(widget);
608 use_background_ =
false;
614 qDebug() <<
"Unknown fancy tab mode" <<
mode;
619 side_layout_->insertWidget(0, bar);
622 for (
const Item &item : std::as_const(items_)) {
624 bar->addSpacer(item.spacer_size_);
627 bar->addTab(item.tab_icon_, item.tab_label_);
631 bar->setCurrentIndex(stack_->currentIndex());
634 use_background_ =
true;
640 MakeTabBar(QTabBar::RoundedNorth,
true,
false,
false);
644 MakeTabBar(QTabBar::RoundedNorth,
false,
true,
false);
648 MakeTabBar(QTabBar::RoundedWest,
true,
true,
true);
649 use_background_ =
true;
653 MakeTabBar(QTabBar::RoundedWest,
true,
true,
false);
657 tab_bar_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
685void FancyTabWidget::AddMenuItem(QSignalMapper* mapper, QActionGroup* group,
686 const QString &text, Mode mode)
688 QAction* action = group->addAction(text);
689 action->setCheckable(
true);
690 mapper->setMapping(action,
mode);
691 connect(action, SIGNAL(triggered()), mapper, SLOT(map()));
694 action->setChecked(
true);
698void FancyTabWidget::MakeTabBar(QTabBar::Shape shape,
bool text,
bool icons,
701 auto* bar =
new QTabBar(
this);
702 bar->setShape(shape);
703 bar->setDocumentMode(
true);
704 bar->setUsesScrollButtons(
true);
706 if (shape == QTabBar::RoundedWest) {
707 bar->setIconSize(QSize(22, 22));
711 bar->setStyle(proxy_style_);
714 if (shape == QTabBar::RoundedNorth) {
715 top_layout_->insertWidget(0, bar);
718 side_layout_->insertWidget(0, bar);
721 for (
const Item &item : std::as_const(items_)) {
726 QString label = item.tab_label_;
727 if (shape == QTabBar::RoundedWest) {
728 label = QFontMetrics(font()).elidedText(label, Qt::ElideMiddle, 100);
733 tab_id = bar->addTab(item.tab_icon_, label);
736 tab_id = bar->addTab(item.tab_icon_, QString());
739 tab_id = bar->addTab(label);
742 bar->setTabToolTip(tab_id, item.tab_label_);
745 bar->setCurrentIndex(stack_->currentIndex());
746 connect(bar, &QTabBar::currentChanged,
this, &FancyTabWidget::ShowWidget);
FancyTabBar(QWidget *parent=nullptr)
QIcon tabIcon(int index) const
void paintTab(QPainter *painter, int tabIndex) const
QSize minimumSizeHint() const override
void addTab(const QIcon &icon, const QString &label)
QString tabToolTip(int index) const
QSize sizeHint() const override
void paintEvent(QPaintEvent *event) override
void setCurrentIndex(int index)
bool validIndex(int index) const
QRect tabRect(int index) const
void mousePressEvent(QMouseEvent *) override
void addSpacer(int size=40)
void setTabToolTip(int index, const QString &toolTip)
void setFader(float value)
void enterEvent(QEnterEvent *event) override
void leaveEvent(QEvent *) override
FancyTab(QWidget *tabbar)
QSize sizeHint() const override
void polish(QWidget *widget) override
bool eventFilter(QObject *o, QEvent *e) override
static void verticalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored=false)
static qreal sidebarFontSize()
static QColor borderColor(bool lightColored=false)
static QColor requestedBaseColor()
static void setBaseColor(const QColor &color)
static QColor panelTextColor(bool lightColored=false)
static QColor sidebarHighlight()
static void drawIconWithShadow(const QIcon &icon, const QRect &rect, QPainter *p, QIcon::Mode iconMode, int radius=3, const QColor &color=QColor(0, 0, 0, 130), const QPoint &offset=QPoint(1, -2))
int value(const QColor &c)
QColor light(const QColor &c, int value)