1 /*
2 * Strawberry Music Player
3 * This file was part of Clementine.
4 * Copyright 2018, Vikram Ambrose <ambroseworks@gmail.com>
5 * Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
6 *
7 * Strawberry is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Strawberry is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "config.h"
23
24 #include <algorithm>
25 #include <utility>
26 #include <chrono>
27
28 #include <QtGlobal>
29 #include <QObject>
30 #include <QApplication>
31 #include <QTabBar>
32 #include <QWidget>
33 #include <QTimer>
34 #include <QList>
35 #include <QMap>
36 #include <QHash>
37 #include <QVariant>
38 #include <QString>
39 #include <QIcon>
40 #include <QPainter>
41 #include <QStylePainter>
42 #include <QColor>
43 #include <QRect>
44 #include <QFont>
45 #include <QFontMetrics>
46 #include <QSize>
47 #include <QPoint>
48 #include <QBrush>
49 #include <QPen>
50 #include <QTransform>
51 #include <QMenu>
52 #include <QAction>
53 #include <QActionGroup>
54 #include <QSettings>
55 #include <QPixmapCache>
56 #include <QLayout>
57 #include <QBoxLayout>
58 #include <QtEvents>
59 #include <QStyle>
60 #include <QCommonStyle>
61 #include <QProxyStyle>
62 #include <QStyleOption>
63 #include <QStyleOptionComplex>
64
65 #include "fancytabwidget.h"
66 #include "core/stylehelper.h"
67 #include "settings/appearancesettingspage.h"
68
69 using namespace std::chrono_literals;
70
71 const int FancyTabWidget::IconSize_LargeSidebar = 40;
72 const int FancyTabWidget::IconSize_SmallSidebar = 32;
73 const int FancyTabWidget::TabSize_LargeSidebarMinWidth = 70;
74
75 class FancyTabBar : public QTabBar { // clazy:exclude=missing-qobject-macro
76
77 private:
78 int mouseHoverTabIndex = -1;
79 QHash<QWidget*, QString> labelCache;
80 QMap<int, QWidget*> spacers;
81
82 public:
FancyTabBar(QWidget * parent=nullptr)83 explicit FancyTabBar(QWidget *parent = nullptr) : QTabBar(parent) {
84 setMouseTracking(true);
85 }
86
sizeHint() const87 QSize sizeHint() const override {
88
89 FancyTabWidget *tabWidget = qobject_cast<FancyTabWidget*>(parentWidget());
90 if (tabWidget->mode() == FancyTabWidget::Mode_Tabs || tabWidget->mode() == FancyTabWidget::Mode_IconOnlyTabs) {
91 return QTabBar::sizeHint();
92 }
93
94 QSize size;
95 int h = 0;
96 for (int i = 0; i < count(); ++i) {
97 if (tabSizeHint(i).width() > size.width()) size.setWidth(tabSizeHint(i).width());
98 h += tabSizeHint(i).height();
99 }
100 size.setHeight(h);
101
102 return size;
103
104 }
105
width() const106 int width() const {
107 FancyTabWidget *tabWidget = qobject_cast<FancyTabWidget*>(parentWidget());
108 if (tabWidget->mode() == FancyTabWidget::Mode_LargeSidebar || tabWidget->mode() == FancyTabWidget::Mode_SmallSidebar) {
109 int w = 0;
110 for (int i = 0; i < count(); ++i) {
111 if (tabSizeHint(i).width() > w) w = tabSizeHint(i).width();
112 }
113 return w;
114 }
115 else {
116 return QTabBar::width();
117 }
118 }
119
120 protected:
tabSizeHint(int index) const121 QSize tabSizeHint(int index) const override {
122
123 FancyTabWidget *tabWidget = qobject_cast<FancyTabWidget*>(parentWidget());
124
125 QSize size;
126 if (tabWidget->mode() == FancyTabWidget::Mode_LargeSidebar) {
127
128 QFont bold_font(font());
129 bold_font.setBold(true);
130 QFontMetrics fm(bold_font);
131
132 // If the text of any tab is wider than the set width then use that instead.
133 int w = std::max(FancyTabWidget::TabSize_LargeSidebarMinWidth, tabWidget->iconsize_largesidebar() + 22);
134 for (int i = 0; i < count(); ++i) {
135 QRect rect = fm.boundingRect(QRect(0, 0, std::max(FancyTabWidget::TabSize_LargeSidebarMinWidth, tabWidget->iconsize_largesidebar() + 22), height()), Qt::TextWordWrap, QTabBar::tabText(i));
136 rect.setWidth(rect.width() + 10);
137 if (rect.width() > w) w = rect.width();
138 }
139
140 QRect rect = fm.boundingRect(QRect(0, 0, w, height()), Qt::TextWordWrap, QTabBar::tabText(index));
141 size = QSize(w, tabWidget->iconsize_largesidebar() + rect.height() + 10);
142 }
143 else if (tabWidget->mode() == FancyTabWidget::Mode_SmallSidebar) {
144
145 QFont bold_font(font());
146 bold_font.setBold(true);
147 QFontMetrics fm(bold_font);
148
149 QRect rect = fm.boundingRect(QRect(0, 0, 100, tabWidget->height()), Qt::AlignHCenter, QTabBar::tabText(index));
150 int w = std::max(tabWidget->iconsize_smallsidebar(), rect.height()) + 15;
151 int h = tabWidget->iconsize_smallsidebar() + rect.width() + 20;
152 size = QSize(w, h);
153 }
154 else {
155 size = QTabBar::tabSizeHint(index);
156 }
157
158 return size;
159
160 }
161
leaveEvent(QEvent * event)162 void leaveEvent(QEvent *event) override {
163 Q_UNUSED(event);
164 mouseHoverTabIndex = -1;
165 update();
166 }
167
mouseMoveEvent(QMouseEvent * event)168 void mouseMoveEvent(QMouseEvent *event) override {
169
170 QPoint pos = event->pos();
171
172 mouseHoverTabIndex = tabAt(pos);
173 if (mouseHoverTabIndex > -1) {
174 update();
175 }
176 QTabBar::mouseMoveEvent(event);
177
178 }
179
paintEvent(QPaintEvent * pe)180 void paintEvent(QPaintEvent *pe) override {
181
182 FancyTabWidget *tabWidget = qobject_cast<FancyTabWidget*>(parentWidget());
183
184 bool verticalTextTabs = false;
185
186 if (tabWidget->mode() == FancyTabWidget::Mode_SmallSidebar) {
187 verticalTextTabs = true;
188 }
189
190 // if LargeSidebar, restore spacers
191 if (tabWidget->mode() == FancyTabWidget::Mode_LargeSidebar && spacers.count() > 0) {
192 QList<int> keys = spacers.keys();
193 for (const int index : keys) {
194 tabWidget->insertTab(index, spacers[index], QIcon(), QString());
195 tabWidget->setTabEnabled(index, false);
196 }
197 spacers.clear();
198 }
199 else if (tabWidget->mode() != FancyTabWidget::Mode_LargeSidebar) {
200 // traverse in the opposite order to save indices of spacers
201 for (int i = count() - 1; i >= 0; --i) {
202 // spacers are disabled tabs
203 if (!isTabEnabled(i) && !spacers.contains(i)) {
204 spacers[i] = tabWidget->widget(i);
205 tabWidget->removeTab(i);
206 --i;
207 }
208 }
209 }
210
211 // Restore any label text that was hidden/cached for the IconOnlyTabs mode
212 if (labelCache.count() > 0 && tabWidget->mode() != FancyTabWidget::Mode_IconOnlyTabs) {
213 for (int i = 0; i < count(); ++i) {
214 setTabToolTip(i, "");
215 setTabText(i, labelCache[tabWidget->widget(i)]);
216 }
217 labelCache.clear();
218 }
219 if (tabWidget->mode() != FancyTabWidget::Mode_LargeSidebar && tabWidget->mode() != FancyTabWidget::Mode_SmallSidebar) {
220 // Cache and hide label text for IconOnlyTabs mode
221 if (tabWidget->mode() == FancyTabWidget::Mode_IconOnlyTabs && labelCache.count() == 0) {
222 for(int i = 0; i < count(); ++i) {
223 labelCache[tabWidget->widget(i)] = tabText(i);
224 setTabToolTip(i, tabText(i));
225 setTabText(i, "");
226 }
227 }
228 QTabBar::paintEvent(pe);
229 return;
230 }
231
232 QStylePainter p(this);
233
234 for (int index = 0; index < count(); ++index) {
235 const bool selected = tabWidget->currentIndex() == index;
236 QRect tabrect = tabRect(index);
237 QRect selectionRect = tabrect;
238 if (selected) {
239 // Selection highlight
240 p.save();
241 QLinearGradient grad(selectionRect.topLeft(), selectionRect.topRight());
242 grad.setColorAt(0, QColor(255, 255, 255, 140));
243 grad.setColorAt(1, QColor(255, 255, 255, 210));
244 p.fillRect(selectionRect.adjusted(0,0,0,-1), grad);
245 p.restore();
246
247 // shadow lines
248 p.setPen(QColor(0, 0, 0, 110));
249 p.drawLine(selectionRect.topLeft() + QPoint(1, -1), selectionRect.topRight() - QPoint(0, 1));
250 p.drawLine(selectionRect.bottomLeft(), selectionRect.bottomRight());
251 p.setPen(QColor(0, 0, 0, 40));
252 p.drawLine(selectionRect.topLeft(), selectionRect.bottomLeft());
253
254 // highlights
255 p.setPen(QColor(255, 255, 255, 50));
256 p.drawLine(selectionRect.topLeft() + QPoint(0, -2), selectionRect.topRight() - QPoint(0, 2));
257 p.drawLine(selectionRect.bottomLeft() + QPoint(0, 1), selectionRect.bottomRight() + QPoint(0, 1));
258 p.setPen(QColor(255, 255, 255, 40));
259 p.drawLine(selectionRect.topLeft() + QPoint(0, 0), selectionRect.topRight());
260 p.drawLine(selectionRect.topRight() + QPoint(0, 1), selectionRect.bottomRight() - QPoint(0, 1));
261 p.drawLine(selectionRect.bottomLeft() + QPoint(0, -1), selectionRect.bottomRight() - QPoint(0, 1));
262
263 }
264
265 // Mouse hover effect
266 if (!selected && index == mouseHoverTabIndex && isTabEnabled(index)) {
267 p.save();
268 QLinearGradient grad(selectionRect.topLeft(), selectionRect.topRight());
269 grad.setColorAt(0, Qt::transparent);
270 grad.setColorAt(0.5, QColor(255, 255, 255, 40));
271 grad.setColorAt(1, Qt::transparent);
272 p.fillRect(selectionRect, grad);
273 p.setPen(QPen(grad, 1.0));
274 p.drawLine(selectionRect.topLeft(), selectionRect.topRight());
275 p.drawLine(selectionRect.bottomRight(), selectionRect.bottomLeft());
276 p.restore();
277 }
278
279 // Label (Icon and Text)
280 {
281 p.save();
282 QTransform m;
283 int textFlags = 0;
284 Qt::Alignment iconFlags;
285
286 QRect tabrectText;
287 QRect tabrectLabel;
288
289 if (verticalTextTabs) {
290 m = QTransform::fromTranslate(tabrect.left(), tabrect.bottom());
291 m.rotate(-90);
292 textFlags = Qt::AlignVCenter;
293 iconFlags = Qt::AlignVCenter;
294
295 tabrectLabel = QRect(QPoint(0, 0), m.mapRect(tabrect).size());
296
297 tabrectText = tabrectLabel;
298 tabrectText.translate(tabWidget->iconsize_smallsidebar() + 8, 0);
299 }
300 else {
301 m = QTransform::fromTranslate(tabrect.left(), tabrect.top());
302 textFlags = Qt::AlignHCenter | Qt::AlignBottom | Qt::TextWordWrap;
303 iconFlags = Qt::AlignHCenter | Qt::AlignTop;
304
305 tabrectLabel = QRect(QPoint(0, 0), m.mapRect(tabrect).size());
306
307 tabrectText = tabrectLabel;
308 tabrectText.translate(0, -5);
309 }
310
311 p.setTransform(m);
312
313 QFont boldFont(p.font());
314 boldFont.setBold(true);
315 p.setFont(boldFont);
316
317 // Text drop shadow color
318 p.setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110) );
319 p.translate(0, 3);
320 p.drawText(tabrectText, textFlags, tabText(index));
321
322 // Text foreground color
323 p.translate(0, -1);
324 p.setPen(selected ? QColor(60, 60, 60) : StyleHelper::panelTextColor());
325 p.drawText(tabrectText, textFlags, tabText(index));
326
327
328 // Draw the icon
329 QRect tabrectIcon;
330 if (verticalTextTabs) {
331 tabrectIcon = tabrectLabel;
332 tabrectIcon.setSize(QSize(tabWidget->iconsize_smallsidebar(), tabWidget->iconsize_smallsidebar()));
333 // Center the icon
334 const int moveRight = (QTabBar::width() - tabWidget->iconsize_smallsidebar()) / 2;
335 tabrectIcon.translate(5, moveRight);
336 }
337 else {
338 tabrectIcon = tabrectLabel;
339 tabrectIcon.setSize(QSize(tabWidget->iconsize_largesidebar(), tabWidget->iconsize_largesidebar()));
340 // Center the icon
341 const int moveRight = (QTabBar::width() - tabWidget->iconsize_largesidebar() -1) / 2;
342 tabrectIcon.translate(moveRight, 5);
343 }
344 tabIcon(index).paint(&p, tabrectIcon, iconFlags);
345 p.restore();
346 }
347 }
348 }
349
350 };
351
352 class TabData : public QObject { // clazy:exclude=missing-qobject-macro
353 public:
TabData(QWidget * widget_view,const QString & name,const QIcon & icon,const QString & label,const int idx,QWidget * parent)354 TabData(QWidget *widget_view, const QString &name, const QIcon &icon, const QString &label, const int idx, QWidget *parent)
355 : QObject(parent),
356 widget_view_(widget_view),
357 name_(name), icon_(icon),
358 label_(label),
359 index_(idx),
360 page_(new QWidget()) {
361 // In order to achieve the same effect as the "Bottom Widget" of the old Nokia based FancyTabWidget a VBoxLayout is used on each page
362 QVBoxLayout *layout = new QVBoxLayout(page_);
363 layout->setSpacing(0);
364 layout->setContentsMargins(0, 0, 0, 0);
365 layout->addWidget(widget_view_);
366 page_->setLayout(layout);
367 }
368
widget_view() const369 QWidget *widget_view() const { return widget_view_; }
name() const370 QString name() const { return name_; }
icon() const371 QIcon icon() const { return icon_; }
label() const372 QString label() const { return label_; }
page() const373 QWidget *page() const { return page_; }
index() const374 int index() const { return index_; }
375
376 private:
377 QWidget *widget_view_;
378 QString name_;
379 QIcon icon_;
380 QString label_;
381 int index_;
382 QWidget *page_;
383
384 };
385
386 // Spacers are just disabled pages
addSpacer()387 void FancyTabWidget::addSpacer() {
388
389 QWidget *spacer = new QWidget(this);
390 const int idx = insertTab(count(), spacer, QIcon(), QString());
391 setTabEnabled(idx, false);
392
393 }
394
setBackgroundPixmap(const QPixmap & pixmap)395 void FancyTabWidget::setBackgroundPixmap(const QPixmap &pixmap) {
396
397 background_pixmap_ = pixmap;
398 update();
399
400 }
401
setCurrentIndex(int idx)402 void FancyTabWidget::setCurrentIndex(int idx) {
403
404 Q_ASSERT(count() > 0);
405
406 if (idx >= count() || idx < 0) idx = 0;
407
408 QWidget *currentPage = widget(idx);
409 QLayout *layout = currentPage->layout();
410 if (bottom_widget_) layout->addWidget(bottom_widget_);
411 QTabWidget::setCurrentIndex(idx);
412
413 }
414
currentTabChanged(const int idx)415 void FancyTabWidget::currentTabChanged(const int idx) {
416
417 QWidget *currentPage = currentWidget();
418 QLayout *layout = currentPage->layout();
419 if (bottom_widget_) layout->addWidget(bottom_widget_);
420 emit CurrentChanged(idx);
421
422 }
423
424 // Override QStyle::subElementRect() and use QCommonStyle to fix a problem with the adwaita style.
425 // The adwaita style is causing the contents of the tabbar to be stretched from top to bottom with space between icons and text.
426 // You can see this on the default Fedora (Gnome) installation.
427
428 class FancyTabWidgetProxyStyle : public QProxyStyle { // clazy:exclude=missing-qobject-macro
429
430 public:
FancyTabWidgetProxyStyle(QStyle * style)431 explicit FancyTabWidgetProxyStyle(QStyle *style) : QProxyStyle(style), common_style_(new QCommonStyle()) {}
~FancyTabWidgetProxyStyle()432 ~FancyTabWidgetProxyStyle() override { common_style_->deleteLater(); }
433
subElementRect(QStyle::SubElement element,const QStyleOption * option,const QWidget * widget=nullptr) const434 QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget = nullptr) const override {
435 if (element == QStyle::SE_TabWidgetTabBar) {
436 QRect proxy_style_rect = QProxyStyle::subElementRect(element, option, widget);
437 // Fix stretched tabbar (adwaita style issue).
438 proxy_style_rect.setHeight(common_style_->subElementRect(element, option, widget).height());
439 return proxy_style_rect;
440 }
441 else {
442 return QProxyStyle::subElementRect(element, option, widget);
443 }
444 }
445
446 private:
447 QCommonStyle *common_style_;
448 };
449
FancyTabWidget(QWidget * parent)450 FancyTabWidget::FancyTabWidget(QWidget *parent)
451 : QTabWidget(parent),
452 style_(nullptr),
453 menu_(nullptr),
454 mode_(Mode_None),
455 bottom_widget_(nullptr),
456 bg_color_system_(true),
457 bg_gradient_(true),
458 iconsize_smallsidebar_(FancyTabWidget::IconSize_SmallSidebar),
459 iconsize_largesidebar_(FancyTabWidget::IconSize_LargeSidebar) {
460
461 FancyTabBar *tabBar = new FancyTabBar(this);
462 setTabBar(tabBar);
463 setTabPosition(QTabWidget::West);
464 setMovable(true);
465 setElideMode(Qt::ElideNone);
466 setUsesScrollButtons(true);
467 if (QApplication::style() && QApplication::style()->objectName().contains(QRegularExpression("^adwaita.*$", QRegularExpression::CaseInsensitiveOption))) {
468 style_ = new FancyTabWidgetProxyStyle(style());
469 setStyle(style_);
470 }
471
472 QObject::connect(tabBar, &FancyTabBar::currentChanged, this, &FancyTabWidget::currentTabChanged);
473
474 }
475
~FancyTabWidget()476 FancyTabWidget::~FancyTabWidget() {
477 if (style_) style_->deleteLater();
478 }
479
Load(const QString & kSettingsGroup)480 void FancyTabWidget::Load(const QString &kSettingsGroup) {
481
482 QSettings s;
483 s.beginGroup(kSettingsGroup);
484 QMultiMap <int, TabData*> tabs;
485 for (TabData *tab : std::as_const(tabs_)) {
486 int idx = s.value("tab_" + tab->name(), tab->index()).toInt();
487 while (tabs.contains(idx)) { ++idx; }
488 tabs.insert(idx, tab);
489 }
490 s.endGroup();
491
492 QMultiMap <int, TabData*> ::iterator i;
493 for (i = tabs.begin(); i != tabs.end(); ++i) {
494 TabData *tab = i.value();
495 const int idx = insertTab(i.key(), tab->page(), tab->icon(), tab->label());
496 tabBar()->setTabData(idx, QVariant(tab->name()));
497 }
498
499 }
500
insertTab(const int idx,QWidget * page,const QIcon & icon,const QString & label)501 int FancyTabWidget::insertTab(const int idx, QWidget *page, const QIcon &icon, const QString &label) {
502 return QTabWidget::insertTab(idx, page, icon, label);
503 }
504
SaveSettings(const QString & kSettingsGroup)505 void FancyTabWidget::SaveSettings(const QString &kSettingsGroup) {
506
507 QSettings s;
508 s.beginGroup(kSettingsGroup);
509
510 s.setValue("tab_mode", mode_);
511 s.setValue("current_tab", currentIndex());
512
513 for (TabData *tab : std::as_const(tabs_)) {
514 QString k = "tab_" + tab->name();
515 int idx = QTabWidget::indexOf(tab->page());
516 if (idx < 0) {
517 if (s.contains(k)) s.remove(k);
518 }
519 else {
520 s.setValue(k, idx);
521 }
522 }
523
524 s.endGroup();
525
526 }
527
ReloadSettings()528 void FancyTabWidget::ReloadSettings() {
529
530 QSettings s;
531 s.beginGroup(AppearanceSettingsPage::kSettingsGroup);
532 bg_color_system_ = s.value(AppearanceSettingsPage::kTabBarSystemColor, false).toBool();
533 bg_gradient_ = s.value(AppearanceSettingsPage::kTabBarGradient, true).toBool();
534 bg_color_ = AppearanceSettingsPage::DefaultTabbarBgColor();
535 if (!bg_color_system_) {
536 bg_color_ = s.value(AppearanceSettingsPage::kTabBarColor, bg_color_).value<QColor>();
537 }
538 iconsize_smallsidebar_ = s.value(AppearanceSettingsPage::kIconSizeTabbarSmallMode, FancyTabWidget::IconSize_SmallSidebar).toInt();
539 iconsize_largesidebar_ = s.value(AppearanceSettingsPage::kIconSizeTabbarLargeMode, FancyTabWidget::IconSize_LargeSidebar).toInt();
540 s.endGroup();
541
542 #ifndef Q_OS_MACOS
543 if (mode() == FancyTabWidget::Mode_LargeSidebar) {
544 setIconSize(QSize(iconsize_largesidebar_, iconsize_largesidebar_));
545 }
546 else {
547 setIconSize(QSize(iconsize_smallsidebar_, iconsize_smallsidebar_));
548 }
549 #endif
550
551 update();
552 tabBarUpdateGeometry();
553
554 }
555
addBottomWidget(QWidget * widget_view)556 void FancyTabWidget::addBottomWidget(QWidget *widget_view) {
557 bottom_widget_ = widget_view;
558 }
559
AddTab(QWidget * widget_view,const QString & name,const QIcon & icon,const QString & label)560 void FancyTabWidget::AddTab(QWidget *widget_view, const QString &name, const QIcon &icon, const QString &label) {
561
562 TabData *tab = new TabData(widget_view, name, icon, label, tabs_.count(), this);
563 tabs_.insert(widget_view, tab);
564
565 }
566
EnableTab(QWidget * widget_view)567 bool FancyTabWidget::EnableTab(QWidget *widget_view) {
568
569 if (!tabs_.contains(widget_view)) return false;
570 TabData *tab = tabs_.value(widget_view);
571
572 if (QTabWidget::indexOf(tab->page()) >= 0) return true;
573 const int idx = QTabWidget::insertTab(count(), tab->page(), tab->icon(), tab->label());
574 tabBar()->setTabData(idx, QVariant(tab->name()));
575
576 return true;
577
578 }
579
DisableTab(QWidget * widget_view)580 bool FancyTabWidget::DisableTab(QWidget *widget_view) {
581
582 if (!tabs_.contains(widget_view)) return false;
583 TabData *tab = tabs_.value(widget_view);
584
585 int idx = QTabWidget::indexOf(tab->page());
586 if (idx < 0) return false;
587
588 removeTab(idx);
589
590 return true;
591
592 }
593
IndexOfTab(QWidget * widget)594 int FancyTabWidget::IndexOfTab(QWidget *widget) {
595 if (!tabs_.contains(widget)) return -1;
596 return QTabWidget::indexOf(tabs_[widget]->page());
597 }
598
paintEvent(QPaintEvent * pe)599 void FancyTabWidget::paintEvent(QPaintEvent *pe) {
600
601 if (mode() != FancyTabWidget::Mode_LargeSidebar && mode() != FancyTabWidget::Mode_SmallSidebar) {
602 QTabWidget::paintEvent(pe);
603 return;
604 }
605
606 QStylePainter painter(this);
607 QRect backgroundRect = rect();
608 backgroundRect.setWidth(tabBar()->width());
609
610 QString key = QString::asprintf("mh_vertical %d %d %d %d %d", backgroundRect.width(), backgroundRect.height(), bg_color_.rgb(), (bg_gradient_ ? 1 : 0), (background_pixmap_.isNull() ? 0 : 1));
611
612 QPixmap pixmap;
613 if (!QPixmapCache::find(key, &pixmap)) {
614
615 pixmap = QPixmap(backgroundRect.size());
616 QPainter p(&pixmap);
617 p.fillRect(backgroundRect, bg_color_);
618
619 // Draw the gradient fill.
620 if (bg_gradient_) {
621
622 QRect rect(0, 0, backgroundRect.width(), backgroundRect.height());
623
624 QColor shadow = StyleHelper::shadowColor(false);
625 QLinearGradient grad(backgroundRect.topRight(), backgroundRect.topLeft());
626 grad.setColorAt(0, bg_color_.lighter(117));
627 grad.setColorAt(1, shadow.darker(109));
628 p.fillRect(rect, grad);
629
630 QColor light(255, 255, 255, 80);
631 p.setPen(light);
632 p.drawLine(rect.topRight() - QPoint(1, 0), rect.bottomRight() - QPoint(1, 0));
633 QColor dark(0, 0, 0, 90);
634 p.setPen(dark);
635 p.drawLine(rect.topLeft(), rect.bottomLeft());
636
637 }
638
639 // Draw the translucent png graphics over the gradient fill
640 if (!background_pixmap_.isNull()) {
641 QRect pixmap_rect(background_pixmap_.rect());
642 pixmap_rect.moveTo(backgroundRect.topLeft());
643
644 while (pixmap_rect.top() < backgroundRect.bottom()) {
645 QRect source_rect(pixmap_rect.intersected(backgroundRect));
646 source_rect.moveTo(0, 0);
647 p.drawPixmap(pixmap_rect.topLeft(), background_pixmap_, source_rect);
648 pixmap_rect.moveTop(pixmap_rect.bottom() - 10);
649 }
650 }
651
652 // Shadow effect of the background
653 QColor light(255, 255, 255, 80);
654 p.setPen(light);
655 p.drawLine(backgroundRect.topRight() - QPoint(1, 0), backgroundRect.bottomRight() - QPoint(1, 0));
656 QColor dark(0, 0, 0, 90);
657 p.setPen(dark);
658 p.drawLine(backgroundRect.topLeft(), backgroundRect.bottomLeft());
659
660 p.setPen(StyleHelper::borderColor());
661 p.drawLine(backgroundRect.topRight(), backgroundRect.bottomRight());
662
663 p.end();
664
665 QPixmapCache::insert(key, pixmap);
666
667 }
668
669 painter.drawPixmap(backgroundRect.topLeft(), pixmap);
670
671 }
672
tabBarUpdateGeometry()673 void FancyTabWidget::tabBarUpdateGeometry() {
674 tabBar()->updateGeometry();
675 }
676
SetMode(FancyTabWidget::Mode mode)677 void FancyTabWidget::SetMode(FancyTabWidget::Mode mode) {
678
679 mode_ = mode;
680
681 if (mode == FancyTabWidget::Mode_Tabs || mode == FancyTabWidget::Mode_IconOnlyTabs) {
682 setTabPosition(QTabWidget::North);
683 }
684 else {
685 setTabPosition(QTabWidget::West);
686 }
687
688 #ifndef Q_OS_MACOS
689 if (mode_ == FancyTabWidget::Mode_LargeSidebar) {
690 setIconSize(QSize(iconsize_largesidebar_, iconsize_largesidebar_));
691 }
692 else {
693 setIconSize(QSize(iconsize_smallsidebar_, iconsize_smallsidebar_));
694 }
695 #endif
696
697 tabBar()->updateGeometry();
698 updateGeometry();
699
700 // There appears to be a bug in QTabBar which causes tabSizeHint to be ignored thus the need for this second shot repaint
701 QTimer::singleShot(1ms, this, &FancyTabWidget::tabBarUpdateGeometry);
702
703 emit ModeChanged(mode);
704
705 }
706
addMenuItem(QActionGroup * group,const QString & text,Mode mode)707 void FancyTabWidget::addMenuItem(QActionGroup *group, const QString &text, Mode mode) {
708
709 QAction *action = group->addAction(text);
710 action->setCheckable(true);
711 QObject::connect(action, &QAction::triggered, this, [this, mode]() { SetMode(mode); });
712
713 if (mode == mode_) action->setChecked(true);
714
715 }
716
contextMenuEvent(QContextMenuEvent * e)717 void FancyTabWidget::contextMenuEvent(QContextMenuEvent *e) {
718
719 if (!menu_) {
720 menu_ = new QMenu(this);
721 QActionGroup *group = new QActionGroup(this);
722 addMenuItem(group, tr("Large sidebar"), Mode_LargeSidebar);
723 addMenuItem(group, tr("Small sidebar"), Mode_SmallSidebar);
724 addMenuItem(group, tr("Plain sidebar"), Mode_PlainSidebar);
725 addMenuItem(group, tr("Tabs on top"), Mode_Tabs);
726 addMenuItem(group, tr("Icons on top"), Mode_IconOnlyTabs);
727 menu_->addActions(group->actions());
728 }
729
730 menu_->popup(e->globalPos());
731
732 }
733