Falkon Develop
Cross-platform Qt-based web browser
tabmanagerdelegate.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2016-2017 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
4* Copyright (C) 2017 David Rosca <nowrep@gmail.com>
5*
6* This program is free software: you can redistribute it and/or modify
7* it under the terms of the GNU General Public License as published by
8* the Free Software Foundation, either version 3 of the License, or
9* (at your option) any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program. If not, see <http://www.gnu.org/licenses/>.
18* ============================================================ */
19#include "tabmanagerdelegate.h"
20#include "tabmanagerwidget.h"
21
22#include <QPainter>
23#include <QApplication>
24#include <QTextLayout>
25
27 : QStyledItemDelegate(parent)
28{
29}
30
31// most of codes taken from QCommonStyle::drawControl() and add our custom text drawer
32void TabManagerDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
33{
34 QStyleOptionViewItem opt = option;
35 initStyleOption(&opt, index);
36
37 const QWidget* w = opt.widget;
38 const QStyle* style = w ? w->style() : QApplication::style();
39 const Qt::LayoutDirection direction = w ? w->layoutDirection() : QApplication::layoutDirection();
40 const bool isActiveOrCaption = index.data(TabItem::ActiveOrCaptionRole).toBool();
41 const bool isSavedTab = index.data(TabItem::SavedRole).toBool();
42
43 const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text;
44
45 QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) && !isSavedTab ? QPalette::Normal : QPalette::Disabled;
46 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) {
47 cg = QPalette::Inactive;
48 }
49
50#ifdef Q_OS_WIN
51 opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text));
52 opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108));
53#endif
54
55 QPalette textPalette = opt.palette;
56 textPalette.setCurrentColorGroup(cg);
57
58 painter->save();
59 painter->setClipRect(opt.rect);
60
61 QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, w);
62 QRect iconRect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, w);
63 QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, w);
64
65 // draw the background
66 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w);
67
68 // draw close button
69 if (index.column() == 1) {
70 if (opt.state & QStyle::State_MouseOver) {
71 static const int buttonSize = 16;
72 static const QPixmap closeTabButton(QStringLiteral(":tabmanager/data/closetab.png"));
73 static const QPixmap addTabButton(QStringLiteral(":tabmanager/data/addtab.png"));
74
75 const QRect rect(opt.rect.right() - buttonSize, (opt.rect.height() - buttonSize) / 2 + opt.rect.y(), buttonSize, buttonSize);
76 painter->drawPixmap(style->visualRect(direction, opt.rect, rect), (index.parent().isValid() ? closeTabButton : addTabButton));
77 }
78
79 painter->restore();
80 return;
81 }
82
83 // draw the check mark
84 if (opt.features & QStyleOptionViewItem::HasCheckIndicator) {
85 QStyleOptionViewItem opt2(opt);
86 opt2.rect = checkRect;
87 opt2.state = opt2.state & ~QStyle::State_HasFocus;
88
89 switch (opt.checkState) {
90 case Qt::Unchecked:
91 opt2.state |= QStyle::State_Off;
92 break;
93 case Qt::PartiallyChecked:
94 opt2.state |= QStyle::State_NoChange;
95 break;
96 case Qt::Checked:
97 opt2.state |= QStyle::State_On;
98 break;
99 }
100 style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt2, painter, w);
101 }
102
103 // draw the icon
104 QIcon::Mode mode = QIcon::Normal;
105 if (!(opt.state & QStyle::State_Enabled))
106 mode = QIcon::Disabled;
107 else if (opt.state & QStyle::State_Selected)
108 mode = QIcon::Selected;
109 QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
110 opt.icon.paint(painter, iconRect, opt.decorationAlignment, mode, state);
111
112 // draw the text
113 if (!opt.text.isEmpty()) {
114 const QString filterText = property("filterText").toString();
115
116 if (opt.state & QStyle::State_Selected) {
117 painter->setPen(opt.palette.color(cg, QPalette::HighlightedText));
118 } else {
119 painter->setPen(opt.palette.color(cg, QPalette::Text));
120 }
121 if (opt.state & QStyle::State_Editing) {
122 painter->setPen(opt.palette.color(cg, QPalette::Text));
123 painter->drawRect(textRect.adjusted(0, 0, -1, -1));
124 }
125
126 if (isSavedTab)
127 opt.font.setItalic(true);
128 else if (isActiveOrCaption)
129 opt.font.setBold(true);
130
131 painter->setFont(opt.font);
132 viewItemDrawText(painter, &opt, textRect, opt.text, textPalette.color(colorRole), filterText);
133 }
134
135 painter->restore();
136}
137
138static bool sizeBiggerThan(const QString &s1, const QString &s2)
139{
140 return s1.size() > s2.size();
141}
142
143static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth)
144{
145 qreal height = 0;
146 qreal widthUsed = 0;
147 textLayout.beginLayout();
148 QTextLine line = textLayout.createLine();
149 if (line.isValid()) {
150 line.setLineWidth(lineWidth);
151 line.setPosition(QPointF(0, height));
152 height += line.height();
153 widthUsed = qMax(widthUsed, line.naturalTextWidth());
154
155 textLayout.endLayout();
156 }
157 return QSizeF(widthUsed, height);
158}
159
160// most of codes taken from QCommonStylePrivate::viewItemDrawText()
161// added highlighting and simplified for single-line textlayouts
162void TabManagerDelegate::viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect,
163 const QString &text, const QColor &color, const QString &searchText) const
164{
165 if (text.isEmpty()) {
166 return;
167 }
168
169 const QWidget* widget = option->widget;
170 const bool isRtlLayout = widget ? widget->isRightToLeft() : QApplication::isRightToLeft();
171 const QStyle* proxyStyle = widget ? widget->style()->proxy() : QApplication::style()->proxy();
172 const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1;
173
174 QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
175 const QFontMetrics fontMetrics(p->font());
176 QString elidedText = fontMetrics.elidedText(text, option->textElideMode, textRect.width());
177 QTextOption textOption;
178 textOption.setWrapMode(QTextOption::NoWrap);
179 textOption.setTextDirection(text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight);
180 textOption.setAlignment(Qt::AlignVCenter | (isRtlLayout ? Qt::AlignRight : Qt::AlignLeft));
181 QTextLayout textLayout;
182 textLayout.setFont(p->font());
183 textLayout.setText(elidedText);
184 textLayout.setTextOption(textOption);
185
186 if (!searchText.isEmpty()) {
187 QList<int> delimiters;
188 QStringList searchStrings = searchText.split(QLatin1Char(' '), Qt::SkipEmptyParts);
189 // Look for longer parts first
190 std::sort(searchStrings.begin(), searchStrings.end(), sizeBiggerThan);
191
192 for (const QString &string : std::as_const(searchStrings)) {
193 int delimiter = text.indexOf(string, 0, Qt::CaseInsensitive);
194
195 while (delimiter != -1) {
196 int start = delimiter;
197 int end = delimiter + string.length();
198 bool alreadyContains = false;
199 for (int i = 0; i < delimiters.count(); ++i) {
200 int dStart = delimiters.at(i);
201 int dEnd = delimiters.at(++i);
202
203 if (dStart <= start && dEnd >= end) {
204 alreadyContains = true;
205 break;
206 }
207 }
208 if (!alreadyContains) {
209 delimiters.append(start);
210 delimiters.append(end);
211 }
212
213 delimiter = text.indexOf(string, end, Qt::CaseInsensitive);
214 }
215 }
216
217 // We need to sort delimiters to properly paint all parts that user typed
218 std::sort(delimiters.begin(), delimiters.end());
219
220 // If we don't find any match, just paint it without any highlight
221 if (!delimiters.isEmpty() && !(delimiters.count() % 2)) {
222 QList<QTextLayout::FormatRange> highlightParts;
223
224 QTextLayout::FormatRange lighterWholeLine;
225 lighterWholeLine.start = 0;
226 lighterWholeLine.length = elidedText.size();
227 QColor lighterColor = color.lighter(130);
228 if (lighterColor == color) {
229 lighterColor = QColor(Qt::gray).darker(180);
230 }
231 lighterWholeLine.format.setForeground(lighterColor);
232 highlightParts << lighterWholeLine;
233
234 while (!delimiters.isEmpty()) {
235 QTextLayout::FormatRange highlightedPart;
236 int start = delimiters.takeFirst();
237 int end = delimiters.takeFirst();
238 highlightedPart.start = start;
239 highlightedPart.length = end - start;
240 highlightedPart.format.setFontWeight(QFont::Bold);
241 highlightedPart.format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
242 highlightedPart.format.setForeground(color);
243
244 highlightParts << highlightedPart;
245 }
246
247 textLayout.setFormats(highlightParts);
248 }
249 }
250
251 // do layout
252 viewItemTextLayout(textLayout, textRect.width());
253
254 if (textLayout.lineCount() <= 0) {
255 return;
256 }
257
258 QTextLine textLine = textLayout.lineAt(0);
259
260 // if elidedText after highlighting is longer
261 // than available width then re-elide it and redo layout
262 int diff = textLine.naturalTextWidth() - textRect.width();
263 if (diff > 0) {
264 elidedText = fontMetrics.elidedText(elidedText, option->textElideMode, textRect.width() - diff);
265
266 textLayout.setText(elidedText);
267 // redo layout
268 viewItemTextLayout(textLayout, textRect.width());
269
270 if (textLayout.lineCount() <= 0) {
271 return;
272 }
273 textLine = textLayout.lineAt(0);
274 }
275
276 // draw line
277 p->setPen(color);
278 qreal width = qMax<qreal>(textRect.width(), textLayout.lineAt(0).width());
279 const QRect &layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, QSize(int(width), int(textLine.height())), textRect);
280 const QPointF &position = layoutRect.topLeft();
281
282 textLine.draw(p, position);
283}
TabManagerDelegate(QObject *parent=nullptr)
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
i
Definition: i18n.py:23
State
State state