1
#include "PrettyItemDelegate.h"
2
#include "../ListModel.h"
3
#include "../fontutils.h"
4
#include "../downloaditem.h"
5
#include "../iconloader/qticonloader.h"
6
#include "../videodefinition.h"
8
#include <QFontMetricsF>
12
const qreal PrettyItemDelegate::THUMB_HEIGHT = 90.0;
13
const qreal PrettyItemDelegate::THUMB_WIDTH = 120.0;
14
const qreal PrettyItemDelegate::PADDING = 10.0;
17
QHash<int, QRect> authorRects;
19
PrettyItemDelegate::PrettyItemDelegate(QObject* parent, bool downloadInfo)
20
: QStyledItemDelegate(parent),
21
downloadInfo(downloadInfo) {
22
boldFont.setBold(true);
23
smallerBoldFont = FontUtils::smallBold();
24
smallerFont = FontUtils::small();
27
progressBar = new QProgressBar(qApp->activeWindow());
28
QPalette palette = progressBar->palette();
29
palette.setColor(QPalette::Window, Qt::transparent);
30
progressBar->setPalette(palette);
31
progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
33
} else createPlayIcon();
36
void PrettyItemDelegate::createPlayIcon() {
37
playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
38
playIcon.fill(Qt::transparent);
39
QPainter painter(&playIcon);
41
polygon << QPoint(PADDING*4, PADDING*2)
42
<< QPoint(THUMB_WIDTH-PADDING*4, THUMB_HEIGHT/2)
43
<< QPoint(PADDING*4, THUMB_HEIGHT-PADDING*2);
44
painter.setRenderHints(QPainter::Antialiasing, true);
45
painter.setBrush(Qt::white);
47
pen.setColor(Qt::white);
48
pen.setWidth(PADDING);
49
pen.setJoinStyle(Qt::RoundJoin);
50
pen.setCapStyle(Qt::RoundCap);
52
painter.drawPolygon(polygon);
55
PrettyItemDelegate::~PrettyItemDelegate() { }
57
QSize PrettyItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
58
return QSize( 256, THUMB_HEIGHT+1.0);
61
void PrettyItemDelegate::paint( QPainter* painter,
62
const QStyleOptionViewItem& option, const QModelIndex& index ) const {
64
int itemType = index.data(ItemTypeRole).toInt();
65
if (itemType == ItemTypeVideo) {
66
QStyleOptionViewItemV4 opt = QStyleOptionViewItemV4(option);
67
initStyleOption(&opt, index);
69
opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
70
paintBody(painter, opt, index);
72
QStyledItemDelegate::paint( painter, option, index );
76
void PrettyItemDelegate::paintBody( QPainter* painter,
77
const QStyleOptionViewItem& option,
78
const QModelIndex& index ) const {
81
painter->translate( option.rect.topLeft() );
84
QRectF line(0, 0, option.rect.width(), option.rect.height());
85
if (downloadInfo) line.setWidth(line.width() / 2);
86
painter->setClipRect(line);
88
const bool isActive = index.data( ActiveTrackRole ).toBool();
89
const bool isSelected = option.state & QStyle::State_Selected;
91
// draw the "current track" highlight underneath the text
92
if (isActive && !isSelected) {
93
paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
96
// get the video metadata
97
const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
98
const Video *video = videoPointer.data();
101
if (!video->thumbnail().isNull()) {
102
painter->drawImage(QRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT), video->thumbnail());
104
// play icon overlayed on the thumb
106
paintPlayIcon(painter);
110
int duration = video->duration();
111
if ( duration > 3600 )
112
timeString = QTime().addSecs(duration).toString("h:mm:ss");
114
timeString = QTime().addSecs(duration).toString("m:ss");
115
drawTime(painter, timeString, line);
119
if (isActive) painter->setFont(boldFont);
123
painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
125
painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
128
QString videoTitle = video->title();
129
QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
130
textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
131
painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
133
painter->setFont(smallerFont);
136
QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
137
QSizeF stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
138
QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
139
QRectF publishedTextBox(textLoc , stringSize);
140
painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
143
bool authorHovered = false;
144
bool authorPressed = false;
145
const bool isHovered = index.data(HoveredItemRole).toBool();
147
authorHovered = index.data(AuthorHoveredRole).toBool();
148
authorPressed = index.data(AuthorPressedRole).toBool();
152
painter->setFont(smallerBoldFont);
155
painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
157
painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
159
QString authorString = video->author();
160
textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
161
stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
162
QRectF authorTextBox(textLoc , stringSize);
163
authorRects.insert(index.row(), authorTextBox.toRect());
164
painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
168
if (video->viewCount() >= 0) {
171
QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
172
textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
173
stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
174
QRectF viewCountTextBox(textLoc , stringSize);
175
painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
181
QString definitionString = VideoDefinition::getDefinitionName(video->getDefinitionCode());
182
textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
183
stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) );
184
QRectF viewCountTextBox(textLoc , stringSize);
185
painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString);
190
QLinearGradient myGradient;
193
QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
195
myPath.addText(baseline, boldFont, authorString);
196
painter->setBrush(palette.color(QPalette::WindowText));
197
painter->setPen(palette.color(QPalette::Dark));
198
painter->setRenderHints (QPainter::Antialiasing, true);
199
painter->drawPath(myPath);
203
painter->setClipping(false);
204
painter->setPen(option.palette.color(QPalette::Midlight));
205
painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
206
if (!video->thumbnail().isNull())
207
painter->setPen(Qt::black);
208
painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
212
if (downloadInfo) paintDownloadInfo(painter, option, index);
216
void PrettyItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
219
QColor highlightColor = palette.color(QPalette::Highlight);
220
QColor backgroundColor = palette.color(QPalette::Base);
221
const float animation = 0.25;
222
const int gradientRange = 16;
224
QColor color2 = QColor::fromHsv(
225
highlightColor.hue(),
226
(int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
227
(int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
229
QColor color1 = QColor::fromHsv(
231
qMax(color2.saturation() - gradientRange, 0),
232
qMin(color2.value() + gradientRange, 255)
234
QRect rect((int) x, (int) y, (int) w, (int) h);
236
painter->setPen(Qt::NoPen);
237
QLinearGradient linearGradient(0, 0, 0, rect.height());
238
linearGradient.setColorAt(0.0, color1);
239
linearGradient.setColorAt(1.0, color2);
240
painter->setBrush(linearGradient);
241
painter->drawRect(rect);
245
void PrettyItemDelegate::paintPlayIcon(QPainter *painter) const {
247
painter->setOpacity(.5);
248
painter->drawPixmap(playIcon.rect(), playIcon);
252
void PrettyItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
253
static const int timePadding = 4;
254
QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
256
textBox.adjust(0, 0, timePadding, 0);
257
// move to bottom right corner of the thumb
258
textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
261
painter->setPen(Qt::NoPen);
262
painter->setBrush(Qt::black);
263
painter->setOpacity(.5);
264
painter->drawRect(textBox);
268
painter->setPen(Qt::white);
269
painter->drawText(textBox, Qt::AlignCenter, time);
273
void PrettyItemDelegate::paintDownloadInfo( QPainter* painter,
274
const QStyleOptionViewItem& option,
275
const QModelIndex& index ) const {
277
// get the video metadata
278
const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
279
const DownloadItem *downloadItem = downloadItemPointer.data();
283
const QRect line(0, 0, option.rect.width() / 2, option.rect.height());
285
painter->translate(option.rect.topLeft());
286
painter->translate(line.width(), 0);
289
DownloadItemStatus status = downloadItem->status();
291
if (status == Downloading) {
292
QString downloaded = DownloadItem::formattedFilesize(downloadItem->bytesReceived());
293
QString total = DownloadItem::formattedFilesize(downloadItem->bytesTotal());
294
QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed());
295
QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
297
message = tr("%1 of %2 (%3) — %4").arg(
303
} else if (status == Starting) {
304
message = tr("Preparing");
305
} else if (status == Failed) {
306
message = tr("Failed") + " — " + downloadItem->errorMessage();
307
} else if (status == Finished) {
308
message = tr("Completed");
309
} else if (status == Idle) {
310
message = tr("Stopped");
313
// progressBar->setPalette(option.palette);
314
if (status == Finished) {
315
progressBar->setValue(100);
316
progressBar->setEnabled(true);
317
} else if (status == Downloading) {
318
progressBar->setValue(downloadItem->currentPercent());
319
progressBar->setEnabled(true);
321
progressBar->setValue(0);
322
progressBar->setEnabled(false);
325
int progressBarWidth = line.width() - PADDING*4 - 16;
326
progressBar->setMaximumWidth(progressBarWidth);
327
progressBar->setMinimumWidth(progressBarWidth);
329
painter->translate(PADDING, PADDING);
330
progressBar->render(painter);
333
bool downloadButtonHovered = false;
334
bool downloadButtonPressed = false;
335
const bool isHovered = index.data(HoveredItemRole).toBool();
337
downloadButtonHovered = index.data(DownloadButtonHoveredRole).toBool();
338
downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool();
340
QIcon::Mode iconMode;
341
if (downloadButtonPressed) iconMode = QIcon::Selected;
342
else if (downloadButtonHovered) iconMode = QIcon::Active;
343
else iconMode = QIcon::Normal;
345
if (status != Finished && status != Failed && status != Idle) {
346
if (downloadButtonHovered) message = tr("Stop downloading");
348
QIcon closeIcon = QtIconLoader::icon("window-close");
349
painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
353
else if (status == Finished) {
354
if (downloadButtonHovered)
356
message = tr("Show in %1").arg("Finder");
358
message = tr("Open parent folder");
361
QIcon searchIcon = QtIconLoader::icon("system-search");
362
painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
366
else if (status == Failed || status == Idle) {
367
if (downloadButtonHovered) message = tr("Restart downloading");
369
QIcon searchIcon = QtIconLoader::icon("view-refresh");
370
painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
374
QRectF textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
375
textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
376
painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
382
QRect PrettyItemDelegate::downloadButtonRect(QRect line) const {
384
line.width() - PADDING*2 - 16,
385
PADDING + progressBar->sizeHint().height() / 2 - 8,
390
QRect PrettyItemDelegate::authorRect(const QModelIndex& index) const {
391
return authorRects.value(index.row());