1 #include "playlistitemdelegate.h"
2 #include "playlistmodel.h"
4 #include "downloaditem.h"
6 #include "videodefinition.h"
9 const qreal PlaylistItemDelegate::THUMB_HEIGHT = 90.0;
10 const qreal PlaylistItemDelegate::THUMB_WIDTH = 120.0;
11 const qreal PlaylistItemDelegate::PADDING = 10.0;
14 QHash<int, QRect> authorRects;
16 PlaylistItemDelegate::PlaylistItemDelegate(QObject* parent, bool downloadInfo)
17 : QStyledItemDelegate(parent),
18 downloadInfo(downloadInfo) {
19 boldFont.setBold(true);
20 smallerBoldFont = FontUtils::smallBold();
21 smallerFont = FontUtils::small();
24 progressBar = new QProgressBar(qApp->activeWindow());
25 QPalette palette = progressBar->palette();
26 palette.setColor(QPalette::Window, Qt::transparent);
27 progressBar->setPalette(palette);
28 progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
30 } else createPlayIcon();
33 void PlaylistItemDelegate::createPlayIcon() {
34 playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
35 playIcon.fill(Qt::transparent);
36 QPainter painter(&playIcon);
38 polygon << QPoint(PADDING*4, PADDING*2)
39 << QPoint(THUMB_WIDTH-PADDING*4, THUMB_HEIGHT/2)
40 << QPoint(PADDING*4, THUMB_HEIGHT-PADDING*2);
41 painter.setRenderHints(QPainter::Antialiasing, true);
42 painter.setBrush(Qt::white);
44 pen.setColor(Qt::white);
45 pen.setWidth(PADDING);
46 pen.setJoinStyle(Qt::RoundJoin);
47 pen.setCapStyle(Qt::RoundCap);
49 painter.drawPolygon(polygon);
52 PlaylistItemDelegate::~PlaylistItemDelegate() { }
54 QSize PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
55 return QSize( 256, THUMB_HEIGHT+1.0);
58 void PlaylistItemDelegate::paint( QPainter* painter,
59 const QStyleOptionViewItem& option, const QModelIndex& index ) const {
61 int itemType = index.data(ItemTypeRole).toInt();
62 if (itemType == ItemTypeVideo) {
63 QStyleOptionViewItemV4 opt = QStyleOptionViewItemV4(option);
64 initStyleOption(&opt, index);
66 opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);
67 paintBody(painter, opt, index);
69 QStyledItemDelegate::paint( painter, option, index );
73 void PlaylistItemDelegate::paintBody( QPainter* painter,
74 const QStyleOptionViewItem& option,
75 const QModelIndex& index ) const {
78 painter->translate( option.rect.topLeft() );
81 QRectF line(0, 0, option.rect.width(), option.rect.height());
82 if (downloadInfo) line.setWidth(line.width() / 2);
83 painter->setClipRect(line);
85 const bool isActive = index.data( ActiveTrackRole ).toBool();
86 const bool isSelected = option.state & QStyle::State_Selected;
88 // draw the "current track" highlight underneath the text
89 if (isActive && !isSelected) {
90 paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
93 // get the video metadata
94 const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
95 const Video *video = videoPointer.data();
98 painter->drawPixmap(0, 0, THUMB_WIDTH, THUMB_HEIGHT, video->thumbnail());
100 // play icon overlayed on the thumb
102 paintPlayIcon(painter);
105 drawTime(painter, video->formattedDuration(), line);
107 if (isActive) painter->setFont(boldFont);
111 painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
113 painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
116 QString videoTitle = video->title();
117 QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
118 textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
119 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
121 painter->setFont(smallerFont);
124 QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
125 QSizeF stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
126 QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
127 QRectF publishedTextBox(textLoc , stringSize);
128 painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
131 bool authorHovered = false;
132 bool authorPressed = false;
133 const bool isHovered = index.data(HoveredItemRole).toBool();
135 authorHovered = index.data(AuthorHoveredRole).toBool();
136 authorPressed = index.data(AuthorPressedRole).toBool();
140 painter->setFont(smallerBoldFont);
143 painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0));
145 painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
147 QString authorString = video->author();
148 textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
149 stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
150 QRectF authorTextBox(textLoc , stringSize);
151 authorRects.insert(index.row(), authorTextBox.toRect());
152 painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
156 if (video->viewCount() >= 0) {
159 QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
160 textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
161 stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
162 QRectF viewCountTextBox(textLoc , stringSize);
163 painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
169 QString definitionString = VideoDefinition::getDefinitionName(video->getDefinitionCode());
170 textLoc.setX(textLoc.x() + stringSize.width() + PADDING);
171 stringSize = QSizeF(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) );
172 QRectF viewCountTextBox(textLoc , stringSize);
173 painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString);
178 QLinearGradient myGradient;
181 QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
183 myPath.addText(baseline, boldFont, authorString);
184 painter->setBrush(palette.color(QPalette::WindowText));
185 painter->setPen(palette.color(QPalette::Dark));
186 painter->setRenderHints (QPainter::Antialiasing, true);
187 painter->drawPath(myPath);
191 painter->setClipping(false);
192 painter->setPen(option.palette.color(QPalette::Midlight));
193 painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
194 if (!video->thumbnail().isNull())
195 painter->setPen(Qt::black);
196 painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
200 if (downloadInfo) paintDownloadInfo(painter, option, index);
204 void PlaylistItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
207 QColor highlightColor = palette.color(QPalette::Highlight);
208 QColor backgroundColor = palette.color(QPalette::Base);
209 const float animation = 0.25;
210 const int gradientRange = 16;
212 QColor color2 = QColor::fromHsv(
213 highlightColor.hue(),
214 (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
215 (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
217 QColor color1 = QColor::fromHsv(
219 qMax(color2.saturation() - gradientRange, 0),
220 qMin(color2.value() + gradientRange, 255)
222 QRect rect((int) x, (int) y, (int) w, (int) h);
224 painter->setPen(Qt::NoPen);
225 QLinearGradient linearGradient(0, 0, 0, rect.height());
226 linearGradient.setColorAt(0.0, color1);
227 linearGradient.setColorAt(1.0, color2);
228 painter->setBrush(linearGradient);
229 painter->drawRect(rect);
233 void PlaylistItemDelegate::paintPlayIcon(QPainter *painter) const {
235 painter->setOpacity(.5);
236 painter->drawPixmap(playIcon.rect(), playIcon);
240 void PlaylistItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
241 static const int timePadding = 4;
242 QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
244 textBox.adjust(0, 0, timePadding, 0);
245 // move to bottom right corner of the thumb
246 textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
249 painter->setPen(Qt::NoPen);
250 painter->setBrush(Qt::black);
251 painter->setOpacity(.5);
252 painter->drawRect(textBox);
256 painter->setPen(Qt::white);
257 painter->drawText(textBox, Qt::AlignCenter, time);
261 void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter,
262 const QStyleOptionViewItem& option,
263 const QModelIndex& index ) const {
265 // get the video metadata
266 const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
267 const DownloadItem *downloadItem = downloadItemPointer.data();
271 const QRect line(0, 0, option.rect.width() / 2, option.rect.height());
273 painter->translate(option.rect.topLeft());
274 painter->translate(line.width(), 0);
277 DownloadItemStatus status = downloadItem->status();
279 if (status == Downloading) {
280 QString downloaded = DownloadItem::formattedFilesize(downloadItem->bytesReceived());
281 QString total = DownloadItem::formattedFilesize(downloadItem->bytesTotal());
282 QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed());
283 QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
285 message = tr("%1 of %2 (%3) — %4").arg(
291 } else if (status == Starting) {
292 message = tr("Preparing");
293 } else if (status == Failed) {
294 message = tr("Failed") + " — " + downloadItem->errorMessage();
295 } else if (status == Finished) {
296 message = tr("Completed");
297 } else if (status == Idle) {
298 message = tr("Stopped");
301 // progressBar->setPalette(option.palette);
302 if (status == Finished) {
303 progressBar->setValue(100);
304 progressBar->setEnabled(true);
305 } else if (status == Downloading) {
306 progressBar->setValue(downloadItem->currentPercent());
307 progressBar->setEnabled(true);
309 progressBar->setValue(0);
310 progressBar->setEnabled(false);
313 int progressBarWidth = line.width() - PADDING*4 - 16;
314 progressBar->setMaximumWidth(progressBarWidth);
315 progressBar->setMinimumWidth(progressBarWidth);
317 painter->translate(PADDING, PADDING);
318 progressBar->render(painter);
321 bool downloadButtonHovered = false;
322 bool downloadButtonPressed = false;
323 const bool isHovered = index.data(HoveredItemRole).toBool();
325 downloadButtonHovered = index.data(DownloadButtonHoveredRole).toBool();
326 downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool();
328 QIcon::Mode iconMode;
329 if (downloadButtonPressed) iconMode = QIcon::Selected;
330 else if (downloadButtonHovered) iconMode = QIcon::Active;
331 else iconMode = QIcon::Normal;
333 if (status != Finished && status != Failed && status != Idle) {
334 if (downloadButtonHovered) message = tr("Stop downloading");
336 QIcon closeIcon = Utils::icon("window-close");
337 painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
341 else if (status == Finished) {
342 if (downloadButtonHovered)
344 message = tr("Show in %1").arg("Finder");
346 message = tr("Open parent folder");
349 QIcon searchIcon = Utils::icon("system-search");
350 painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
354 else if (status == Failed || status == Idle) {
355 if (downloadButtonHovered) message = tr("Restart downloading");
357 QIcon searchIcon = Utils::icon("view-refresh");
358 painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
362 QRectF textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
363 textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
364 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
370 QRect PlaylistItemDelegate::downloadButtonRect(QRect line) const {
372 line.width() - PADDING*2 - 16,
373 PADDING + progressBar->sizeHint().height() / 2 - 8,
378 QRect PlaylistItemDelegate::authorRect(const QModelIndex& index) const {
379 return authorRects.value(index.row());