1 #include "PrettyItemDelegate.h"
2 #include "../ListModel.h"
3 #include "../fontutils.h"
4 #include "../downloaditem.h"
5 #include "../iconloader/qticonloader.h"
7 #include <QFontMetricsF>
10 const qreal PrettyItemDelegate::THUMB_HEIGHT = 90.0;
11 const qreal PrettyItemDelegate::THUMB_WIDTH = 120.0;
12 const qreal PrettyItemDelegate::PADDING = 10.0;
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();
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);
28 } else createPlayIcon();
31 void PrettyItemDelegate::createPlayIcon() {
32 playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT);
33 playIcon.fill(Qt::transparent);
34 QPainter painter(&playIcon);
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);
42 pen.setColor(Qt::white);
43 pen.setWidth(PADDING);
44 pen.setJoinStyle(Qt::RoundJoin);
45 pen.setCapStyle(Qt::RoundCap);
47 painter.drawPolygon(polygon);
50 PrettyItemDelegate::~PrettyItemDelegate() { }
52 QSize PrettyItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const {
53 return QSize( 256, THUMB_HEIGHT+1.0);
56 void PrettyItemDelegate::paint( QPainter* painter,
57 const QStyleOptionViewItem& option, const QModelIndex& index ) const {
59 int itemType = index.data(ItemTypeRole).toInt();
60 if (itemType == ItemTypeVideo) {
61 QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );
62 paintBody( painter, option, index );
64 QStyledItemDelegate::paint( painter, option, index );
68 void PrettyItemDelegate::paintBody( QPainter* painter,
69 const QStyleOptionViewItem& option,
70 const QModelIndex& index ) const {
73 painter->translate( option.rect.topLeft() );
76 QRectF line(0, 0, option.rect.width(), option.rect.height());
77 if (downloadInfo) line.setWidth(line.width() / 2);
78 painter->setClipRect(line);
80 const bool isActive = index.data( ActiveTrackRole ).toBool();
81 const bool isSelected = option.state & QStyle::State_Selected;
83 // draw the "current track" highlight underneath the text
84 if (isActive && !isSelected) {
85 paintActiveOverlay(painter, line.x(), line.y(), line.width(), line.height());
88 #if defined(APP_MAC) | defined(APP_WIN)
90 paintSelectedOverlay(painter, line.x(), line.y(), line.width(), line.height());
94 // get the video metadata
95 const VideoPointer videoPointer = index.data( VideoRole ).value<VideoPointer>();
96 const Video *video = videoPointer.data();
99 if (!video->thumbnail().isNull()) {
100 painter->drawImage(QRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT), video->thumbnail());
102 // play icon overlayed on the thumb
104 paintPlayIcon(painter);
108 int duration = video->duration();
109 if ( duration > 3600 )
110 timeString = QTime().addSecs(duration).toString("h:mm:ss");
112 timeString = QTime().addSecs(duration).toString("m:ss");
113 drawTime(painter, timeString, line);
117 if (isActive) painter->setFont(boldFont);
121 painter->setPen(QPen(option.palette.brush(QPalette::HighlightedText), 0));
123 painter->setPen(QPen(option.palette.brush(QPalette::Text), 0));
126 QString videoTitle = video->title();
127 QRectF textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, -2 * PADDING, -PADDING);
128 textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
129 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, videoTitle);
131 painter->setFont(smallerFont);
134 QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate);
135 QSizeF publishedStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) );
136 QPointF textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height());
137 QRectF publishedTextBox( textLoc , publishedStringSize);
138 painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString);
142 painter->setFont(smallerBoldFont);
143 if (!isSelected && !isActive)
144 painter->setPen(QPen(option.palette.brush(QPalette::Mid), 0));
145 QString authorString = video->author();
146 QSizeF authorStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) );
147 textLoc.setX(textLoc.x() + publishedStringSize.width() + PADDING);
148 QRectF authorTextBox( textLoc , authorStringSize);
149 painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString);
153 if (video->viewCount() >= 0) {
156 QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount()));
157 QSizeF viewCountStringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) );
158 textLoc.setX(textLoc.x() + authorStringSize.width() + PADDING);
159 QRectF viewCountTextBox( textLoc , viewCountStringSize);
160 painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString);
165 QLinearGradient myGradient;
168 QPointF baseline(authorTextBox.x(), authorTextBox.y() + authorTextBox.height());
170 myPath.addText(baseline, boldFont, authorString);
171 painter->setBrush(palette.color(QPalette::WindowText));
172 painter->setPen(palette.color(QPalette::Dark));
173 painter->setRenderHints (QPainter::Antialiasing, true);
174 painter->drawPath(myPath);
178 painter->setClipping(false);
179 painter->setPen(option.palette.color(QPalette::Midlight));
180 painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT);
181 if (!video->thumbnail().isNull())
182 painter->setPen(Qt::black);
183 painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT);
187 if (downloadInfo) paintDownloadInfo(painter, option, index);
191 void PrettyItemDelegate::paintActiveOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
194 QColor highlightColor = palette.color(QPalette::Highlight);
195 QColor backgroundColor = palette.color(QPalette::Base);
196 const float animation = 0.25;
197 const int gradientRange = 16;
199 QColor color2 = QColor::fromHsv(
200 highlightColor.hue(),
201 (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation),
202 (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation)
204 QColor color1 = QColor::fromHsv(
206 qMax(color2.saturation() - gradientRange, 0),
207 qMin(color2.value() + gradientRange, 255)
209 QRect rect((int) x, (int) y, (int) w, (int) h);
211 painter->setPen(Qt::NoPen);
212 QLinearGradient linearGradient(0, 0, 0, rect.height());
213 linearGradient.setColorAt(0.0, color1);
214 linearGradient.setColorAt(1.0, color2);
215 painter->setBrush(linearGradient);
216 painter->drawRect(rect);
220 void PrettyItemDelegate::paintSelectedOverlay( QPainter *painter, qreal x, qreal y, qreal w, qreal h ) const {
221 QColor color1 = QColor::fromRgb(0x69, 0xa6, 0xd9);
222 QColor color2 = QColor::fromRgb(0x14, 0x6b, 0xd4);
223 QRect rect((int) x, (int) y, (int) w, (int) h);
225 painter->setPen(Qt::NoPen);
226 QLinearGradient linearGradient(0, 0, 0, rect.height());
227 linearGradient.setColorAt(0.0, color1);
228 linearGradient.setColorAt(1.0, color2);
229 painter->setBrush(linearGradient);
230 painter->drawRect(rect);
234 void PrettyItemDelegate::paintPlayIcon(QPainter *painter) const {
236 painter->setOpacity(.5);
237 painter->drawPixmap(playIcon.rect(), playIcon);
241 void PrettyItemDelegate::drawTime(QPainter *painter, QString time, QRectF line) const {
242 static const int timePadding = 4;
243 QRectF textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time);
245 textBox.adjust(0, 0, timePadding, 0);
246 // move to bottom right corner of the thumb
247 textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height());
250 painter->setPen(Qt::NoPen);
251 painter->setBrush(Qt::black);
252 painter->setOpacity(.5);
253 painter->drawRect(textBox);
257 painter->setPen(Qt::white);
258 painter->drawText(textBox, Qt::AlignCenter, time);
262 void PrettyItemDelegate::paintDownloadInfo( QPainter* painter,
263 const QStyleOptionViewItem& option,
264 const QModelIndex& index ) const {
266 // get the video metadata
267 const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value<DownloadItemPointer>();
268 const DownloadItem *downloadItem = downloadItemPointer.data();
272 const QRect line(0, 0, option.rect.width() / 2, option.rect.height());
274 painter->translate(option.rect.topLeft());
275 painter->translate(line.width(), 0);
278 DownloadItemStatus status = downloadItem->status();
280 if (status == Downloading) {
281 QString downloaded = DownloadItem::formattedFilesize(downloadItem->bytesReceived());
282 QString total = DownloadItem::formattedFilesize(downloadItem->bytesTotal());
283 QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed());
284 QString eta = DownloadItem::formattedTime(downloadItem->remainingTime());
286 message = tr("%1 of %2 (%3) — %4").arg(
292 } else if (status == Starting) {
293 message = tr("Preparing");
294 } else if (status == Failed) {
295 message = tr("Failed") + " — " + downloadItem->errorMessage();
296 } else if (status == Finished) {
297 message = tr("Completed");
298 } else if (status == Idle) {
299 message = tr("Stopped");
302 // progressBar->setPalette(option.palette);
303 if (status == Finished) {
304 progressBar->setValue(100);
305 progressBar->setEnabled(true);
306 } else if (status == Downloading) {
307 progressBar->setValue(downloadItem->currentPercent());
308 progressBar->setEnabled(true);
310 progressBar->setValue(0);
311 progressBar->setEnabled(false);
314 int progressBarWidth = line.width() - PADDING*4 - 16;
315 progressBar->setMaximumWidth(progressBarWidth);
316 progressBar->setMinimumWidth(progressBarWidth);
318 painter->translate(PADDING, PADDING);
319 progressBar->render(painter);
322 bool downloadButtonHovered = false;
323 bool downloadButtonPressed = false;
324 const bool isHovered = index.data(HoveredItemRole).toBool();
326 downloadButtonHovered = index.data(DownloadButtonHoveredRole).toBool();
327 downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool();
329 QIcon::Mode iconMode;
330 if (downloadButtonPressed) iconMode = QIcon::Selected;
331 else if (downloadButtonHovered) iconMode = QIcon::Active;
332 else iconMode = QIcon::Normal;
334 if (status != Finished && status != Failed && status != Idle) {
335 if (downloadButtonHovered) message = tr("Stop downloading");
337 QIcon closeIcon = QtIconLoader::icon("window-close");
338 painter->drawPixmap(downloadButtonRect(line), closeIcon.pixmap(16, 16, iconMode));
342 else if (status == Finished) {
343 if (downloadButtonHovered)
345 message = tr("Show in %1").arg("Finder");
347 message = tr("Open parent folder");
350 QIcon searchIcon = QtIconLoader::icon("system-search");
351 painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
355 else if (status == Failed || status == Idle) {
356 if (downloadButtonHovered) message = tr("Restart downloading");
358 QIcon searchIcon = QtIconLoader::icon("view-refresh");
359 painter->drawPixmap(downloadButtonRect(line), searchIcon.pixmap(16, 16, iconMode));
363 QRectF textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING);
364 textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
365 painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message);
371 QRect PrettyItemDelegate::downloadButtonRect(QRect line) const {
373 line.width() - PADDING*2 - 16,
374 PADDING + progressBar->sizeHint().height() / 2 - 8,