X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fplaylistitemdelegate.cpp;h=c111113c886ae32d204deba90ecb1409fec0aa9f;hb=cc9017d7466db69a72db245456ce0562ffff449b;hp=717c7e57d64cbe7ee82a8318bd552d645a8bb41d;hpb=fd2de342562fc7565ee7437636eb10c4456e0368;p=minitube diff --git a/src/playlistitemdelegate.cpp b/src/playlistitemdelegate.cpp index 717c7e5..c111113 100644 --- a/src/playlistitemdelegate.cpp +++ b/src/playlistitemdelegate.cpp @@ -19,23 +19,33 @@ along with Minitube. If not, see . $END_LICENSE */ #include "playlistitemdelegate.h" -#include "playlistmodel.h" -#include "fontutils.h" +#include "datautils.h" #include "downloaditem.h" +#include "fontutils.h" #include "iconutils.h" -#include "videodefinition.h" +#include "playlistmodel.h" +#include "playlistview.h" #include "video.h" +#include "videodefinition.h" -const int PlaylistItemDelegate::THUMB_HEIGHT = 90; -const int PlaylistItemDelegate::THUMB_WIDTH = 160; -const int PlaylistItemDelegate::PADDING = 10; +const int PlaylistItemDelegate::thumbHeight = 90; +const int PlaylistItemDelegate::thumbWidth = 160; +const int PlaylistItemDelegate::padding = 8; -PlaylistItemDelegate::PlaylistItemDelegate(QObject* parent, bool downloadInfo) - : QStyledItemDelegate(parent), - downloadInfo(downloadInfo), - progressBar(0) { +namespace { + +bool drawElidedText(QPainter *painter, const QRect &textBox, const int flags, const QString &text) { + QString elidedText = + painter->fontMetrics().elidedText(text, Qt::ElideRight, textBox.width(), flags); + painter->drawText(textBox, 0, elidedText); + return elidedText.length() < text.length(); +} +} // namespace + +PlaylistItemDelegate::PlaylistItemDelegate(QObject *parent, bool downloadInfo) + : QStyledItemDelegate(parent), downloadInfo(downloadInfo), progressBar(nullptr) { + listView = qobject_cast(parent); - boldFont.setBold(true); smallerBoldFont = FontUtils::smallBold(); smallerFont = FontUtils::small(); @@ -46,7 +56,8 @@ PlaylistItemDelegate::PlaylistItemDelegate(QObject* parent, bool downloadInfo) progressBar->setPalette(palette); progressBar->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); progressBar->hide(); - } else createPlayIcon(); + } else + createPlayIcon(); } PlaylistItemDelegate::~PlaylistItemDelegate() { @@ -54,25 +65,27 @@ PlaylistItemDelegate::~PlaylistItemDelegate() { } void PlaylistItemDelegate::createPlayIcon() { - playIcon = QPixmap(THUMB_WIDTH, THUMB_HEIGHT); + qreal maxRatio = 2.0; + playIcon = QPixmap(thumbWidth * maxRatio, thumbHeight * maxRatio); + playIcon.setDevicePixelRatio(maxRatio); playIcon.fill(Qt::transparent); - QPixmap tempPixmap(THUMB_WIDTH, THUMB_HEIGHT); + QPixmap tempPixmap(thumbWidth * maxRatio, thumbHeight * maxRatio); + tempPixmap.setDevicePixelRatio(maxRatio); tempPixmap.fill(Qt::transparent); QPainter painter(&tempPixmap); painter.setRenderHints(QPainter::Antialiasing, true); - const int hPadding = PADDING*6; - const int vPadding = PADDING*2; + const int hPadding = padding * 6; + const int vPadding = padding * 2; QPolygon polygon; - polygon << QPoint(hPadding, vPadding) - << QPoint(THUMB_WIDTH-hPadding, THUMB_HEIGHT/2) - << QPoint(hPadding, THUMB_HEIGHT-vPadding); + polygon << QPoint(hPadding, vPadding) << QPoint(thumbWidth - hPadding, thumbHeight / 2) + << QPoint(hPadding, thumbHeight - vPadding); painter.setBrush(Qt::white); QPen pen; pen.setColor(Qt::white); - pen.setWidth(PADDING); + pen.setWidth(padding); pen.setJoinStyle(Qt::RoundJoin); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); @@ -84,67 +97,58 @@ void PlaylistItemDelegate::createPlayIcon() { painter2.drawPixmap(0, 0, tempPixmap); } -QSize PlaylistItemDelegate::sizeHint( const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/ ) const { - return QSize(THUMB_WIDTH, THUMB_HEIGHT + 1); +QSize PlaylistItemDelegate::sizeHint(const QStyleOptionViewItem & /*option*/, + const QModelIndex & /*index*/) const { + return QSize(thumbWidth, thumbHeight); } -void PlaylistItemDelegate::paint( QPainter* painter, - const QStyleOptionViewItem& option, const QModelIndex& index ) const { - +void PlaylistItemDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { int itemType = index.data(ItemTypeRole).toInt(); - if (itemType == ItemTypeVideo) { - QStyleOptionViewItemV4 opt = QStyleOptionViewItemV4(option); - initStyleOption(&opt, index); - opt.text = ""; - opt.widget->style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget); - paintBody(painter, opt, index); - } else - QStyledItemDelegate::paint( painter, option, index ); - + if (itemType == ItemTypeVideo) + paintBody(painter, option, index); + else + QStyledItemDelegate::paint(painter, option, index); } -void PlaylistItemDelegate::paintBody( QPainter* painter, - const QStyleOptionViewItem& option, - const QModelIndex& index ) const { +void PlaylistItemDelegate::paintBody(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { + const bool isSelected = option.state & QStyle::State_Selected; + if (isSelected) + QApplication::style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter); + painter->save(); - painter->translate( option.rect.topLeft() ); + painter->translate(option.rect.topLeft()); QRect line(0, 0, option.rect.width(), option.rect.height()); if (downloadInfo) line.setWidth(line.width() / 2); - const bool isActive = index.data( ActiveTrackRole ).toBool(); - const bool isSelected = option.state & QStyle::State_Selected; - - // draw the "current track" highlight underneath the text - if (isActive && !isSelected) - paintActiveOverlay(painter, line); + const bool isActive = index.data(ActiveTrackRole).toBool(); // get the video metadata - const VideoPointer videoPointer = index.data( VideoRole ).value(); - const Video *video = videoPointer.data(); - - // thumb - painter->drawPixmap(0, 0, video->thumbnail()); + const Video *video = index.data(VideoRole).value().data(); - // play icon overlayed on the thumb - if (isActive) - painter->drawPixmap(playIcon.rect(), playIcon); - - // time - if (video->duration() > 0) - drawTime(painter, video->formattedDuration(), line); + // draw the "current track" highlight underneath the text + if (isActive && !isSelected) paintActiveOverlay(painter, option, line); - // separator - painter->setPen(option.palette.color(QPalette::Midlight)); - painter->drawLine(THUMB_WIDTH, THUMB_HEIGHT, option.rect.width(), THUMB_HEIGHT); - if (!video->thumbnail().isNull()) - painter->setPen(Qt::black); - painter->drawLine(0, THUMB_HEIGHT, THUMB_WIDTH-1, THUMB_HEIGHT); + // thumb + const QPixmap &thumb = video->getThumbnail(); + if (!thumb.isNull()) { + painter->drawPixmap(0, 0, thumb); + if (video->getDuration() > 0) drawTime(painter, video->getFormattedDuration(), line); + } - if (line.width() > THUMB_WIDTH + 60) { + const bool thumbsOnly = line.width() <= thumbWidth + 60; + const bool isHovered = index.data(HoveredItemRole).toBool(); - // if (isActive) painter->setFont(boldFont); + // play icon overlayed on the thumb + bool needPlayIcon = isActive; + if (thumbsOnly) needPlayIcon = needPlayIcon && !isHovered; + if (needPlayIcon) painter->drawPixmap(0, 0, playIcon); + if (!thumbsOnly) { // text color if (isSelected) painter->setPen(QPen(option.palette.highlightedText(), 0)); @@ -152,146 +156,135 @@ void PlaylistItemDelegate::paintBody( QPainter* painter, painter->setPen(QPen(option.palette.text(), 0)); // title - QString videoTitle = video->title(); - QString v = videoTitle; - const int flags = Qt::AlignTop | Qt::TextWordWrap; - QRect textBox = line.adjusted(PADDING+THUMB_WIDTH, PADDING, 0, 0); - textBox = painter->boundingRect(textBox, flags, v); - while (textBox.height() > 55 && v.length() > 10) { - videoTitle.truncate(videoTitle.length() - 1); - v = videoTitle; - v = v.trimmed().append("..."); - textBox = painter->boundingRect(textBox, flags, v); + QStringRef title(&video->getTitle()); + QString elidedTitle = video->getTitle(); + static const int titleFlags = Qt::AlignTop | Qt::TextWordWrap; + QRect textBox = line.adjusted(padding + thumbWidth, padding, -padding, 0); + textBox = painter->boundingRect(textBox, titleFlags, elidedTitle); + while (textBox.height() > 55 && elidedTitle.length() > 10) { +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + title = title.left(title.length() - 1); +#elif QT_VERSION < QT_VERSION_CHECK(5, 8, 0) + title.truncate(title.length() - 1); +#else + title.chop(1); +#endif + elidedTitle = title.trimmed() + QStringLiteral("…"); + textBox = painter->boundingRect(textBox, titleFlags, elidedTitle); } - painter->drawText(textBox, flags, v); + painter->drawText(textBox, titleFlags, elidedTitle); painter->setFont(smallerFont); + painter->setOpacity(.5); + QFontMetrics fontMetrics = painter->fontMetrics(); + static const int flags = Qt::AlignLeft | Qt::AlignTop; // published date - QString publishedString = video->published().date().toString(Qt::DefaultLocaleShortDate); - QSize stringSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, publishedString ) ); - QPoint textLoc(PADDING+THUMB_WIDTH, PADDING*2 + textBox.height()); - QRect publishedTextBox(textLoc , stringSize); - painter->drawText(publishedTextBox, Qt::AlignLeft | Qt::AlignTop, publishedString); - - if (line.width() > publishedTextBox.x() + publishedTextBox.width()*2) { - - // author - bool authorHovered = false; - bool authorPressed = false; - const bool isHovered = index.data(HoveredItemRole).toBool(); - if (isHovered) { - authorHovered = index.data(AuthorHoveredRole).toBool(); - authorPressed = index.data(AuthorPressedRole).toBool(); - } + const QString &published = video->getFormattedPublished(); + QSize textSize(fontMetrics.size(Qt::TextSingleLine, published)); + QPoint textPoint(padding + thumbWidth, padding * 2 + textBox.height()); + textBox = QRect(textPoint, textSize); + painter->drawText(textBox, flags, published); + + bool elided = false; + + // author + if (!listView || listView->isClickableAuthors()) { + bool authorHovered = isHovered && index.data(AuthorHoveredRole).toBool(); painter->save(); painter->setFont(smallerBoldFont); if (!isSelected) { if (authorHovered) painter->setPen(QPen(option.palette.brush(QPalette::Highlight), 0)); - else - painter->setOpacity(.5); } - QString authorString = video->channelTitle(); - textLoc.setX(textLoc.x() + stringSize.width() + PADDING); - stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, authorString ) ); - QRect authorTextBox(textLoc , stringSize); - authorRects.insert(index.row(), authorTextBox); - painter->drawText(authorTextBox, Qt::AlignLeft | Qt::AlignTop, authorString); + const QString &author = video->getChannelTitle(); + textPoint.setX(textBox.right() + padding); + textSize = QSize(painter->fontMetrics().size(Qt::TextSingleLine, author)); + textBox = QRect(textPoint, textSize); + authorRects.insert(index.row(), textBox); + if (textBox.right() > line.width() - padding) { + textBox.setRight(line.width()); + elided = drawElidedText(painter, textBox, flags, author); + } else { + painter->drawText(textBox, flags, author); + } painter->restore(); + } - if (line.width() > authorTextBox.x() + 50) { - - // view count - if (video->viewCount() >= 0) { - QLocale locale; - QString viewCountString = tr("%1 views").arg(locale.toString(video->viewCount())); - textLoc.setX(textLoc.x() + stringSize.width() + PADDING); - stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, viewCountString ) ); - QRect viewCountTextBox(textLoc , stringSize); - painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, viewCountString); - } - - if (downloadInfo) { - QString definitionString = VideoDefinition::getDefinitionName(video->getDefinitionCode()); - textLoc.setX(textLoc.x() + stringSize.width() + PADDING); - stringSize = QSize(QFontMetrics(painter->font()).size( Qt::TextSingleLine, definitionString ) ); - QRect viewCountTextBox(textLoc , stringSize); - painter->drawText(viewCountTextBox, Qt::AlignLeft | Qt::AlignBottom, definitionString); - } - + // view count + if (video->getViewCount() > 0) { + const QString &viewCount = video->getFormattedViewCount(); + textPoint.setX(textBox.right() + padding); + textSize = QSize(fontMetrics.size(Qt::TextSingleLine, viewCount)); + if (elided || textPoint.x() + textSize.width() > line.width() - padding) { + textPoint.setX(thumbWidth + padding); + textPoint.setY(textPoint.y() + textSize.height() + padding); } + textBox = QRect(textPoint, textSize); + if (textBox.bottom() <= line.height()) { + painter->drawText(textBox, flags, viewCount); + } + } + if (downloadInfo) { + const QString &def = VideoDefinition::forCode(video->getDefinitionCode()).getName(); + textPoint.setX(textBox.right() + padding); + textSize = QSize(fontMetrics.size(Qt::TextSingleLine, def)); + textBox = QRect(textPoint, textSize); + painter->drawText(textBox, flags, def); } } else { - - const bool isHovered = index.data(HoveredItemRole).toBool(); - if (!isActive && isHovered) { + // thumbs only + if (isHovered) { painter->setFont(smallerFont); painter->setPen(Qt::white); - QString videoTitle = video->title(); - QString v = videoTitle; - const int flags = Qt::AlignTop | Qt::TextWordWrap; - QRect textBox(PADDING, PADDING, THUMB_WIDTH - PADDING*2, THUMB_HEIGHT - PADDING*2); - textBox = painter->boundingRect(textBox, flags, v); - while (textBox.height() > THUMB_HEIGHT && v.length() > 10) { - videoTitle.truncate(videoTitle.length() - 1); - v = videoTitle; - v = v.trimmed().append("..."); - textBox = painter->boundingRect(textBox, flags, v); + QStringRef title(&video->getTitle()); + QString elidedTitle = video->getTitle(); + static const int titleFlags = Qt::AlignTop | Qt::TextWordWrap; + QRect textBox(padding, padding, thumbWidth - padding * 2, thumbHeight - padding * 2); + textBox = painter->boundingRect(textBox, titleFlags, elidedTitle); + while (textBox.height() > 55 && elidedTitle.length() > 10) { +#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0) + title = title.left(title.length() - 1); +#elif QT_VERSION < QT_VERSION_CHECK(5, 8, 0) + title.truncate(title.length() - 1); +#else + title.chop(1); +#endif + elidedTitle = title.trimmed() + QStringLiteral("…"); + textBox = painter->boundingRect(textBox, titleFlags, elidedTitle); } - painter->fillRect(QRect(0, 0, THUMB_WIDTH, textBox.height() + PADDING*2), QColor(0, 0, 0, 128)); - painter->drawText(textBox, flags, v); + painter->fillRect(QRect(0, 0, thumbWidth, textBox.height() + padding * 2), + QColor(0, 0, 0, 128)); + painter->drawText(textBox, titleFlags, elidedTitle); } - } painter->restore(); if (downloadInfo) paintDownloadInfo(painter, option, index); - } -void PlaylistItemDelegate::paintActiveOverlay(QPainter *painter, const QRect &line) const { - static QLinearGradient linearGradient; - static bool initialized = false; - - if (!initialized) { - QPalette palette; - QColor highlightColor = palette.color(QPalette::Highlight); - QColor backgroundColor = palette.color(QPalette::Base); - const float animation = 0.4; - const int gradientRange = 16; - - QColor color2 = QColor::fromHsv( - highlightColor.hue(), - (int) (backgroundColor.saturation() * (1.0f - animation) + highlightColor.saturation() * animation), - (int) (backgroundColor.value() * (1.0f - animation) + highlightColor.value() * animation) - ); - QColor color1 = QColor::fromHsv( - color2.hue(), - qMax(color2.saturation() - gradientRange, 0), - qMin(color2.value() + gradientRange, 255) - ); - - linearGradient = QLinearGradient(0, 0, 0, THUMB_HEIGHT); - linearGradient.setColorAt(0.0, color1); - linearGradient.setColorAt(1.0, color2); - initialized = true; - } - - painter->fillRect(line, linearGradient); +void PlaylistItemDelegate::paintActiveOverlay(QPainter *painter, + const QStyleOptionViewItem &option, + const QRect &line) const { + painter->save(); + painter->setOpacity(.2); + painter->fillRect(line, option.palette.highlight()); + painter->restore(); } -void PlaylistItemDelegate::drawTime(QPainter *painter, const QString &time, const QRect &line) const { +void PlaylistItemDelegate::drawTime(QPainter *painter, + const QString &time, + const QRect &line) const { static const int timePadding = 4; QRect textBox = painter->boundingRect(line, Qt::AlignLeft | Qt::AlignTop, time); // add padding textBox.adjust(0, 0, timePadding, 0); // move to bottom right corner of the thumb - textBox.translate(THUMB_WIDTH - textBox.width(), THUMB_HEIGHT - textBox.height()); + textBox.translate(thumbWidth - textBox.width(), thumbHeight - textBox.height()); painter->save(); painter->setPen(Qt::NoPen); @@ -306,12 +299,12 @@ void PlaylistItemDelegate::drawTime(QPainter *painter, const QString &time, cons painter->restore(); } -void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter, - const QStyleOptionViewItem& option, - const QModelIndex& index ) const { - +void PlaylistItemDelegate::paintDownloadInfo(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const { // get the video metadata - const DownloadItemPointer downloadItemPointer = index.data(DownloadItemRole).value(); + const DownloadItemPointer downloadItemPointer = + index.data(DownloadItemRole).value(); const DownloadItem *downloadItem = downloadItemPointer.data(); painter->save(); @@ -330,12 +323,7 @@ void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter, QString speed = DownloadItem::formattedSpeed(downloadItem->currentSpeed()); QString eta = DownloadItem::formattedTime(downloadItem->remainingTime()); - message = tr("%1 of %2 (%3) — %4").arg( - downloaded, - total, - speed, - eta - ); + message = tr("%1 of %2 (%3) — %4").arg(downloaded, total, speed, eta); } else if (status == Starting) { message = tr("Preparing"); } else if (status == Failed) { @@ -358,11 +346,11 @@ void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter, progressBar->setEnabled(false); } - int progressBarWidth = line.width() - PADDING*4 - 16; + int progressBarWidth = line.width() - padding * 4 - 16; progressBar->setMaximumWidth(progressBarWidth); progressBar->setMinimumWidth(progressBarWidth); painter->save(); - painter->translate(PADDING, PADDING); + painter->translate(padding, padding); progressBar->render(painter); painter->restore(); @@ -374,9 +362,12 @@ void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter, downloadButtonPressed = index.data(DownloadButtonPressedRole).toBool(); } QIcon::Mode iconMode; - if (downloadButtonPressed) iconMode = QIcon::Selected; - else if (downloadButtonHovered) iconMode = QIcon::Active; - else iconMode = QIcon::Normal; + if (downloadButtonPressed) + iconMode = QIcon::Selected; + else if (downloadButtonHovered) + iconMode = QIcon::Active; + else + iconMode = QIcon::Normal; if (status != Finished && status != Failed && status != Idle) { if (downloadButtonHovered) message = tr("Stop downloading"); @@ -407,21 +398,20 @@ void PlaylistItemDelegate::paintDownloadInfo( QPainter* painter, painter->restore(); } - QRect textBox = line.adjusted(PADDING, PADDING*2 + progressBar->sizeHint().height(), -2 * PADDING, -PADDING); - textBox = painter->boundingRect( textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message); + QRect textBox = line.adjusted(padding, padding * 2 + progressBar->sizeHint().height(), + -2 * padding, -padding); + textBox = painter->boundingRect(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, + message); painter->drawText(textBox, Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, message); painter->restore(); } QRect PlaylistItemDelegate::downloadButtonRect(const QRect &line) const { - return QRect( - line.width() - PADDING*2 - 16, - PADDING + progressBar->sizeHint().height() / 2 - 8, - 16, - 16); + return QRect(line.width() - padding * 2 - 16, + padding + progressBar->sizeHint().height() / 2 - 8, 16, 16); } -QRect PlaylistItemDelegate::authorRect(const QModelIndex& index) const { +QRect PlaylistItemDelegate::authorRect(const QModelIndex &index) const { return authorRects.value(index.row()); }