Falkon Develop
Cross-platform Qt-based web browser
tabtreedelegate.cpp
Go to the documentation of this file.
1/* ============================================================
2* VerticalTabs plugin for Falkon
3* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
18#include "tabtreedelegate.h"
19#include "tabtreeview.h"
20#include "loadinganimator.h"
21
22#include "tabmodel.h"
23#include "tabicon.h"
24
25#include <QTabBar>
26#include <QPainter>
27#include <QPushButton>
28#include <QApplication>
29
30// TabTreeCloseButton
32 : QAbstractButton(parent)
33{
34 int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, nullptr, this);
35 int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, nullptr, this);
36 resize(width, height);
37}
38
40{
41 return m_showOnNormal;
42}
43
45{
46 m_showOnNormal = show;
47}
48
50{
51 return m_showOnHovered;
52}
53
55{
56 m_showOnHovered = show;
57}
58
60{
61 return m_showOnSelected;
62}
63
65{
66 m_showOnSelected = show;
67}
68
69bool TabTreeCloseButton::isVisible(bool hovered, bool selected) const
70{
71 if (hovered && selected) {
72 return m_showOnHovered || m_showOnSelected;
73 } else if (selected) {
74 return m_showOnSelected;
75 } else if (hovered) {
76 return m_showOnHovered;
77 } else {
78 return m_showOnNormal;
79 }
80}
81
82void TabTreeCloseButton::paintEvent(QPaintEvent *)
83{
84}
85
86// TabTreeDelegate
88 : QStyledItemDelegate()
89 , m_view(view)
90{
91 m_padding = qMax(5, m_view->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1);
92 m_indentation = 15;
93
94 m_loadingAnimator = new LoadingAnimator(this);
95 connect(m_loadingAnimator, &LoadingAnimator::updateIndex, m_view, &TabTreeView::updateIndex);
96
97 // Needed to make it stylable the same way as real tabbar close button
98 auto *tabBar = new QTabBar(m_view);
99 tabBar->setObjectName(QSL("tabtree_tabbar"));
100 tabBar->lower();
101
102 m_closeButton = new TabTreeCloseButton(tabBar);
103 m_closeButton->lower();
104}
105
106static int indexDepth(QModelIndex index)
107{
108 int i = 0;
109 while (index.parent().isValid()) {
110 index = index.parent();
111 i++;
112 }
113 return i;
114}
115
116QRect TabTreeDelegate::expandButtonRect(const QModelIndex &index) const
117{
118 const QRect rect = m_view->visualRect(index);
119 const int depth = indexDepth(index);
120 return QRect(m_indentation * depth, rect.y(), m_indentation, rect.height());
121}
122
123QRect TabTreeDelegate::audioButtonRect(const QModelIndex &index) const
124{
125 if (!index.data(TabModel::AudioPlayingRole).toBool() && !index.data(TabModel::AudioMutedRole).toBool()) {
126 return QRect();
127 }
128 const QRect rect = m_view->visualRect(index);
129 const int center = rect.height() / 2 + rect.top();
130 const int rightPosition = rect.right() - m_padding * 2 - 16;
131 return QRect(rightPosition - 16, center - 16 / 2, 16, 16);
132}
133
134QRect TabTreeDelegate::closeButtonRect(const QModelIndex &index) const
135{
136 const QRect rect = m_view->visualRect(index);
137 const int center = rect.height() / 2 + rect.top();
138 QSize size = m_closeButton->size();
139 size.setHeight(qMin(rect.height() - m_padding, size.height()));
140 return QRect(QPoint(rect.right() - m_padding - size.width(), center - size.height() / 2), size);
141}
142
143void TabTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
144{
145 const QWidget *w = option.widget;
146 const QStyle *style = w ? w->style() : m_view->style();
147
148 const bool expanded = m_view->isExpanded(index);
149 const bool children = m_view->model()->rowCount(index) > 0;
150 const int depth = indexDepth(index);
151 const bool isRestoredTab = index.data(TabModel::RestoredRole).toBool();
152
153 QStyleOptionViewItem opt = option;
154 initStyleOption(&opt, index);
155 m_view->adjustStyleOption(&opt);
156
157 const int height = opt.rect.height();
158 const int center = height / 2 + opt.rect.top();
159
160 int leftPosition = opt.rect.left() + m_indentation + m_indentation * depth + m_padding;
161 int rightPosition = opt.rect.right() - m_padding * 2 - m_closeButton->size().width();
162
163 const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text;
164
165 QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
166 if (!isRestoredTab) {
167 cg = QPalette::Disabled;
168 }
169 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) {
170 cg = QPalette::Inactive;
171 }
172
173#ifdef Q_OS_WIN
174 opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text));
175 opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108));
176#endif
177
178 QPalette textPalette = opt.palette;
179 textPalette.setCurrentColorGroup(cg);
180
181 const bool hovered = opt.state.testFlag(QStyle::State_MouseOver);
182 const bool selected = opt.state.testFlag(QStyle::State_Selected);
183
184 // Draw background
185 if (m_view->backgroundIndentation()) {
186 opt.rect.moveLeft(m_indentation * depth);
187 opt.rect.setWidth(opt.rect.width() - m_indentation * depth);
188 }
189 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w);
190
191 // Draw expand button
192 if (children) {
193 QStyleOptionViewItem o = opt;
194 o.state &= ~QStyle::State_MouseOver;
195 o.rect.moveLeft(m_indentation * depth);
196 o.rect.setWidth(m_indentation);
197 style->drawPrimitive(expanded ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight, &o, painter, w);
198 }
199
200 // Draw icon
201 const int iconSize = 16;
202 const int iconYPos = center - (iconSize / 2);
203 QRect iconRect(leftPosition, iconYPos, iconSize, iconSize);
204 QPixmap pixmap;
205 if (index.data(TabModel::LoadingRole).toBool()) {
206 pixmap = m_loadingAnimator->pixmap(index);
207 } else {
208 pixmap = index.data(Qt::DecorationRole).value<QIcon>().pixmap(iconSize);
209 }
210 painter->drawPixmap(iconRect, pixmap);
211 leftPosition += iconRect.width() + m_padding;
212
213 // Draw close button
214 if (m_closeButton->isVisible(hovered, selected)) {
215 QStyleOptionButton o;
216 o.initFrom(m_closeButton);
217
218 const bool hovered = closeButtonRect(index).contains(m_view->viewport()->mapFromGlobal(QCursor::pos()));
219 const bool pressed = hovered && QApplication::mouseButtons() == Qt::LeftButton;
220
221 QSize closeSize = QSize(o.rect.size().width(), qMin(height - m_padding, o.rect.size().height()));
222 QPoint pos(opt.rect.right() - m_padding - closeSize.width(), center - closeSize.height() / 2);
223 o.rect = QRect(pos, closeSize);
224 o.state |= QStyle::State_AutoRaise | QStyle::State_Enabled | QStyle::State_Selected;
225 o.state.setFlag(QStyle::State_Raised, hovered && !pressed);
226 o.state.setFlag(QStyle::State_Sunken, pressed);
227 o.state.setFlag(QStyle::State_MouseOver, hovered);
228 style->drawPrimitive(QStyle::PE_IndicatorTabClose, &o, painter, m_closeButton);
229 }
230
231 // Draw audio icon
232 const bool audioMuted = index.data(TabModel::AudioMutedRole).toBool();
233 const bool audioPlaying = index.data(TabModel::AudioPlayingRole).toBool();
234 if (audioMuted || audioPlaying) {
235 QSize audioSize(16, 16);
236 QPoint pos(rightPosition - audioSize.width(), center - audioSize.height() / 2);
237 QRect audioRect(pos, audioSize);
238 painter->drawPixmap(audioRect, audioMuted ? TabIcon::data()->audioMutedPixmap : TabIcon::data()->audioPlayingPixmap);
239 rightPosition -= audioSize.width() + m_padding;
240 }
241
242 // Draw title
243 QRect titleRect(leftPosition, center - opt.fontMetrics.height() / 2, opt.rect.width(), opt.fontMetrics.height());
244 titleRect.setRight(rightPosition - m_padding);
245 QString title = opt.fontMetrics.elidedText(index.data().toString(), Qt::ElideRight, titleRect.width());
246 style->drawItemText(painter, titleRect, Qt::AlignLeft, textPalette, true, title, colorRole);
247}
248
249QSize TabTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
250{
251 QStyleOptionViewItem opt(option);
252 initStyleOption(&opt, index);
253
254 return QSize(200, m_padding * 2 + opt.fontMetrics.height());
255}
void updateIndex(const QModelIndex &index)
QPixmap pixmap(const QModelIndex &index)
static Data * data()
Definition: tabicon.cpp:99
@ AudioPlayingRole
Definition: tabmodel.h:61
@ LoadingRole
Definition: tabmodel.h:60
@ RestoredRole
Definition: tabmodel.h:58
@ AudioMutedRole
Definition: tabmodel.h:62
void setShowOnHovered(int show)
bool isVisible(bool hovered, bool selected) const
void setShowOnNormal(int show)
void setShowOnSelected(int show)
TabTreeCloseButton(QWidget *parent=nullptr)
QRect audioButtonRect(const QModelIndex &index) const
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
QRect closeButtonRect(const QModelIndex &index) const
TabTreeDelegate(TabTreeView *view)
QRect expandButtonRect(const QModelIndex &index) const
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
int backgroundIndentation
Definition: tabtreeview.h:31
void updateIndex(const QModelIndex &index)
void adjustStyleOption(QStyleOptionViewItem *option)
i
Definition: i18n.py:23
#define QSL(x)
Definition: qzcommon.h:40