]> git.sur5r.net Git - minitube/blob - src/playlist/PrettyItemDelegate.cpp
Imported Upstream version 1.6
[minitube] / src / playlist / PrettyItemDelegate.cpp
1 #include "PrettyItemDelegate.h"
2 #include "../ListModel.h"
3 #include "../fontutils.h"
4 #include "../downloaditem.h"
5 #include "../iconloader/qticonloader.h"
6
7 #include <QFontMetricsF>
8 #include <QPainter>
9
10 const qreal PrettyItemDelegate::THUMB_HEIGHT = 90.0;
11 const qreal PrettyItemDelegate::THUMB_WIDTH = 120.0;
12 const qreal PrettyItemDelegate::PADDING = 10.0;
13
14 PrettyItemDelegate::PrettyItemDelegate(QObject* parent, bool downloadInfo)
15     : QStyledItemDelegate(parent),
16     downloadInfo(downloadInfo) {
17     boldFont.setBold(true);
18     smallerBoldFont = FontUtils::smallBold();
19     smallerFont = FontUtils::small();
20
21     if (downloadInfo) {
22         progressBar = new QProgressBar(qApp->activeWindow());
23         QPalette palette = progressBar->palette();
24         palette.setColor(QPalette::Window, Qt::transparent);
25         progressBar->setPalette(palette);
26         progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
27         progressBar->hide();
28     } else createPlayIcon();
29 }
30
31 void PrettyItemDelegate::createPlayIcon() {
32     playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
33     playIcon.fill(Qt::transparent);
34     QPainter painter(&playIcon);
35     QPolygon polygon;
36     polygon << QPoint(PADDING*4, PADDING*2)
37             << QPoint(THUMB_WIDTH-PADDING*4, THUMB_HEIGHT/2)
38             << QPoint(PADDING*4, THUMB_HEIGHT-PADDING*2);
39     painter.setRenderHints(QPainter::Antialiasing, true);
40     painter.setBrush(Qt::white);
41     QPen pen;
42     pen.setColor(Qt::white);
43     pen.setWidth(PADDING);
44     pen.setJoinStyle(Qt::RoundJoin);
45     pen.setCapStyle(Qt::RoundCap);
46     painter.setPen(pen);
47     painter.drawPolygon(polygon);
48 }
49
50 PrettyItemDelegate::~PrettyItemDelegate() { }
51
52 QSize PrettyItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
53     return QSize( 256, THUMB_HEIGHT+1.0);
54 }
55
56 void PrettyItemDelegate::paint( QPainter* painter,
57                                 const QStyleOptionViewItem& option, const QModelIndex& index ) const {
58
59     int itemType = index.data(ItemTypeRole).toInt();
60     if (itemType == ItemTypeVideo) {
61         QStyleOptionViewItemV4 opt = QStyleOptionViewItemV4(option);
62         initStyleOption(&opt, index);
63         opt.text = "";
64         opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
65         paintBody(painter, opt, index);
66     } else
67         QStyledItemDelegate::paint( painter, option, index );
68
69 }
70
71 void PrettyItemDelegate::paintBody( QPainter* painter,
72                                     const QStyleOptionViewItem& option,
73                                     const QModelIndex& index ) const {
74
75     painter->save();
76     painter->translate( option.rect.topLeft() );
77
78
79     QRectF line(0, 0, option.rect.width(), option.rect.height());
80     if (downloadInfo) line.setWidth(line.width() / 2);
81     painter->setClipRect(line);
82
83     const bool isActive = index.data( ActiveTrackRole ).toBool();
84     const bool isSelected = option.state & QStyle::State_Selected;
85
86     // draw the "current track" highlight underneath the text
87     if (isActive && !isSelected) {
88         paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
89     }
90
91     // get the video metadata
92     const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
93     const Video *video = videoPointer.data();
94
95     // thumb
96     if (!video->thumbnail().isNull()) {
97         painter->drawImage(QRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT), video->thumbnail());
98
99         // play icon overlayed on the thumb
100         if (isActive)
101             paintPlayIcon(painter);
102
103         // time
104         QString timeString;
105         int duration = video->duration();
106         if ( duration > 3600 )
107             timeString = QTime().addSecs(duration).toString("h:mm:ss");
108         else
109             timeString = QTime().addSecs(duration).toString("m:ss");
110         drawTime(painter, timeString, line);
111
112     }
113
114     if (isActive) painter->setFont(boldFont);
115
116     // text color
117     if (isSelected)
118         painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
119     else
120         painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
121
122     // title
123     QString videoTitle = video->title();
124     QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
125     textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
126     painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
127
128     painter->setFont(smallerFont);
129
130     // published date
131     QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
132     QSizeF publishedStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
133     QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
134     QRectF publishedTextBox( textLoc , publishedStringSize);
135     painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
136
137     // author
138     painter->save();
139     painter->setFont(smallerBoldFont);
140     if (!isSelected && !isActive)
141         painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
142     QString authorString = video->author();
143     QSizeF authorStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
144     textLoc.setX(textLoc.x() + publishedStringSize.width() + PADDING);
145     QRectF authorTextBox( textLoc , authorStringSize);
146     painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
147     painter->restore();
148
149     // view count
150     if (video->viewCount() >= 0) {
151         painter->save();
152         QLocale locale;
153         QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
154         QSizeF viewCountStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
155         textLoc.setX(textLoc.x() + authorStringSize.width() + PADDING);
156         QRectF viewCountTextBox( textLoc , viewCountStringSize);
157         painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
158         painter->restore();
159     }
160
161     /*
162     QLinearGradient myGradient;
163     QPen myPen;
164     QFont myFont;
165     QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
166     QPainterPath myPath;
167     myPath.addText(baseline, boldFont, authorString);
168     painter->setBrush(palette.color(QPalette::WindowText));
169     painter->setPen(palette.color(QPalette::Dark));
170     painter->setRenderHints (QPainter::Antialiasing, true);
171     painter->drawPath(myPath);
172     */
173
174     // separator
175     painter->setClipping(false);
176     painter->setPen(option.palette.color(QPalette::Midlight));
177     painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
178     if (!video->thumbnail().isNull())
179         painter->setPen(Qt::black);
180     painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
181
182     painter->restore();
183
184     if (downloadInfo) paintDownloadInfo(painter, option, index);
185
186 }
187
188 void PrettyItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
189
190     QPalette palette;
191     QColor highlightColor = palette.color(QPalette::Highlight);
192     QColor backgroundColor = palette.color(QPalette::Base);
193     const float animation = 0.25;
194     const int gradientRange = 16;
195
196     QColor color2 = QColor::fromHsv(
197             highlightColor.hue(),
198             (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
199             (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
200             );
201     QColor color1 = QColor::fromHsv(
202             color2.hue(),
203             qMax(color2.saturation() - gradientRange, 0),
204             qMin(color2.value() + gradientRange, 255)
205             );
206     QRect rect((int) x, (int) y, (int) w, (int) h);
207     painter->save();
208     painter->setPen(Qt::NoPen);
209     QLinearGradient linearGradient(0, 0, 0, rect.height());
210     linearGradient.setColorAt(0.0, color1);
211     linearGradient.setColorAt(1.0, color2);
212     painter->setBrush(linearGradient);
213     painter->drawRect(rect);
214     painter->restore();
215 }
216
217 void PrettyItemDelegate::paintPlayIcon(QPainter *painter) const {
218     painter->save();
219     painter->setOpacity(.5);
220     painter->drawPixmap(playIcon.rect(), playIcon);
221     painter->restore();
222 }
223
224 void PrettyItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
225     static const int timePadding = 4;
226     QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
227     // add padding
228     textBox.adjust(0, 0, timePadding, 0);
229     // move to bottom right corner of the thumb
230     textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
231
232     painter->save();
233     painter->setPen(Qt::NoPen);
234     painter->setBrush(Qt::black);
235     painter->setOpacity(.5);
236     painter->drawRect(textBox);
237     painter->restore();
238
239     painter->save();
240     painter->setPen(Qt::white);
241     painter->drawText(textBox, Qt::AlignCenter, time);
242     painter->restore();
243 }
244
245 void PrettyItemDelegate::paintDownloadInfo( QPainter* painter,
246                                             const QStyleOptionViewItem& option,
247                                             const QModelIndex& index ) const {
248
249     // get the video metadata
250     const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
251     const DownloadItem *downloadItem = downloadItemPointer.data();
252
253     painter->save();
254
255     const QRect line(0, 0, option.rect.width() / 2, option.rect.height());
256
257     painter->translate(option.rect.topLeft());
258     painter->translate(line.width(), 0);
259
260     QString message;
261     DownloadItemStatus status = downloadItem->status();
262
263     if (status == Downloading) {
264         QString downloaded = DownloadItem::formattedFilesize(downloadItem->bytesReceived());
265         QString total = DownloadItem::formattedFilesize(downloadItem->bytesTotal());
266         QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed());
267         QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
268
269         message = tr("%1 of %2 (%3) — %4").arg(
270                 downloaded,
271                 total,
272                 speed,
273                 eta
274                 );
275     } else if (status == Starting) {
276         message = tr("Preparing");
277     } else if (status == Failed) {
278         message = tr("Failed") + " — " + downloadItem->errorMessage();
279     } else if (status == Finished) {
280         message = tr("Completed");
281     } else if (status == Idle) {
282         message = tr("Stopped");
283     }
284
285     // progressBar->setPalette(option.palette);
286     if (status == Finished) {
287         progressBar->setValue(100);
288         progressBar->setEnabled(true);
289     } else if (status == Downloading) {
290         progressBar->setValue(downloadItem->currentPercent());
291         progressBar->setEnabled(true);
292     } else {
293         progressBar->setValue(0);
294         progressBar->setEnabled(false);
295     }
296
297     int progressBarWidth = line.width() - PADDING*4 - 16;
298     progressBar->setMaximumWidth(progressBarWidth);
299     progressBar->setMinimumWidth(progressBarWidth);
300     painter->save();
301     painter->translate(PADDING, PADDING);
302     progressBar->render(painter);
303     painter->restore();
304
305     bool downloadButtonHovered = false;
306     bool downloadButtonPressed = false;
307     const bool isHovered = index.data(HoveredItemRole).toBool();
308     if (isHovered) {
309         downloadButtonHovered = index.data(DownloadButtonHoveredRole).toBool();
310         downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool();
311     }
312     QIcon::Mode iconMode;
313     if (downloadButtonPressed) iconMode = QIcon::Selected;
314     else if (downloadButtonHovered) iconMode = QIcon::Active;
315     else iconMode = QIcon::Normal;
316
317     if (status != Finished && status != Failed && status != Idle) {
318         if (downloadButtonHovered) message = tr("Stop downloading");
319         painter->save();
320         QIcon closeIcon = QtIconLoader::icon("window-close");
321         painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
322         painter->restore();
323     }
324
325     else if (status == Finished) {
326         if (downloadButtonHovered)
327 #ifdef APP_MAC
328         message = tr("Show in %1").arg("Finder");
329 #else
330         message = tr("Open parent folder");
331 #endif
332         painter->save();
333         QIcon searchIcon = QtIconLoader::icon("system-search");
334         painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
335         painter->restore();
336     }
337
338     else if (status == Failed || status == Idle) {
339         if (downloadButtonHovered) message = tr("Restart downloading");
340         painter->save();
341         QIcon searchIcon = QtIconLoader::icon("view-refresh");
342         painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
343         painter->restore();
344     }
345
346     QRectF textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
347     textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
348     painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
349
350     painter->restore();
351
352 }
353
354 QRect PrettyItemDelegate::downloadButtonRect(QRect line) const {
355     return QRect(
356             line.width() - PADDING*2 - 16,
357             PADDING + progressBar->sizeHint().height() / 2 - 8,
358             16,
359             16);
360 }