Qt: make grid view customizable by stylesheet

This commit is contained in:
CozmoP 2018-12-23 00:45:00 +01:00
parent 7bb23d15bd
commit ca9952ceef
5 changed files with 188 additions and 61 deletions

View File

@ -6,21 +6,20 @@
/* http://www.informit.com/articles/article.aspx?p=1613548 */ /* http://www.informit.com/articles/article.aspx?p=1613548 */
ThumbnailDelegate::ThumbnailDelegate(QObject* parent) : ThumbnailDelegate::ThumbnailDelegate(const GridItem &gridItem, QObject* parent) :
QStyledItemDelegate(parent) QStyledItemDelegate(parent), m_style(gridItem)
{ {
} }
void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const
{ {
QStyleOptionViewItem opt = option; QStyleOptionViewItem opt = option;
const QWidget *widget = opt.widget; const QWidget *widget = opt.widget;
QStyle *style = widget->style(); QStyle *style = widget->style();
int margin = 11; int padding = m_style.padding;
int textMargin = 4; int textTopMargin = 4; /* Qt seemingly reports -4 the actual line height. */
int textHeight = painter->fontMetrics().height() + margin + margin; int textHeight = painter->fontMetrics().height() + padding + padding;
QRect rect = opt.rect; QRect rect = opt.rect;
QRect adjusted = rect.adjusted(margin, margin, -margin, -textHeight + textMargin); QRect adjusted = rect.adjusted(padding, padding, -padding, -textHeight + textTopMargin);
QPixmap pixmap = index.data(PlaylistModel::THUMBNAIL).value<QPixmap>(); QPixmap pixmap = index.data(PlaylistModel::THUMBNAIL).value<QPixmap>();
painter->save(); painter->save();
@ -34,14 +33,14 @@ void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &opt
if (!pixmap.isNull()) if (!pixmap.isNull())
{ {
QPixmap pixmapScaled = pixmap.scaled(adjusted.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); QPixmap pixmapScaled = pixmap.scaled(adjusted.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | Qt::AlignBottom, pixmapScaled); style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | m_style.thumbnailVerticalAlignmentFlag, pixmapScaled);
} }
/* draw the text */ /* draw the text */
if (!opt.text.isEmpty()) if (!opt.text.isEmpty())
{ {
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
QRect textRect = QRect(rect.x() + margin, rect.y() + adjusted.height() - textMargin + margin, rect.width() - 2 * margin, textHeight); QRect textRect = QRect(rect.x() + padding, rect.y() + adjusted.height() - textTopMargin + padding, rect.width() - 2 * padding, textHeight);
QString elidedText = painter->fontMetrics().elidedText(opt.text, opt.textElideMode, textRect.width(), Qt::TextShowMnemonic); QString elidedText = painter->fontMetrics().elidedText(opt.text, opt.textElideMode, textRect.width(), Qt::TextShowMnemonic);
if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active))
@ -95,7 +94,8 @@ void GridView::calculateRectsIfNecessary() const
const int maxWidth = viewport()->width(); const int maxWidth = viewport()->width();
switch (m_viewMode) { switch (m_viewMode)
{
case Anchored: case Anchored:
{ {
int columns = (maxWidth - m_spacing) / (m_size + m_spacing); int columns = (maxWidth - m_spacing) / (m_size + m_spacing);
@ -114,7 +114,6 @@ void GridView::calculateRectsIfNecessary() const
m_rectForRow[row] = QRectF(x, y, m_size, m_size); m_rectForRow[row] = QRectF(x, y, m_size, m_size);
x = nextX; x = nextX;
} }
m_idealHeight = y + m_size + m_spacing;
} }
break; break;
} }
@ -137,7 +136,6 @@ void GridView::calculateRectsIfNecessary() const
m_rectForRow[row] = QRectF(x, y, m_size, m_size); m_rectForRow[row] = QRectF(x, y, m_size, m_size);
x = nextX; x = nextX;
} }
m_idealHeight = y + m_size + m_spacing;
} }
break; break;
} }
@ -156,7 +154,7 @@ void GridView::calculateRectsIfNecessary() const
} }
break; break;
} }
m_idealHeight = y + m_size + m_spacing;
m_hashIsDirty = false; m_hashIsDirty = false;
viewport()->update(); viewport()->update();
} }
@ -407,3 +405,77 @@ void GridView::updateGeometries()
emit(visibleItemsChangedMaybe()); emit(visibleItemsChangedMaybe());
} }
QString GridView::getLayout() const
{
switch (m_viewMode)
{
case Simple:
return "simple";
case Anchored:
return "anchored";
case Centered:
default:
return "centered";
}
}
void GridView::setLayout(QString layout)
{
if (layout == "anchored")
m_viewMode = Anchored;
else if (layout == "centered")
m_viewMode = Centered;
else if (layout == "fixed")
m_viewMode = Simple;
}
int GridView::getSpacing() const
{
return m_spacing;
}
void GridView::setSpacing(const int spacing)
{
m_spacing = spacing;
}
GridItem::GridItem(QWidget* parent) : QWidget(parent)
, thumbnailVerticalAlignmentFlag(Qt::AlignBottom)
, padding(11)
{
}
int GridItem::getPadding() const
{
return padding;
}
void GridItem::setPadding(const int value)
{
padding = value;
}
QString GridItem::getThumbnailVerticalAlign() const
{
switch (thumbnailVerticalAlignmentFlag)
{
case Qt::AlignTop:
return "top";
case Qt::AlignVCenter:
return "center";
case Qt::AlignBottom:
default:
return "bottom";
}
}
void GridItem::setThumbnailVerticalAlign(const QString valign)
{
if (valign == "top")
thumbnailVerticalAlignmentFlag = Qt::AlignTop;
else if (valign == "center")
thumbnailVerticalAlignmentFlag = Qt::AlignVCenter;
else if (valign == "bottom")
thumbnailVerticalAlignmentFlag = Qt::AlignBottom;
}

View File

@ -4,19 +4,32 @@
#include <QAbstractItemView> #include <QAbstractItemView>
#include <QStyledItemDelegate> #include <QStyledItemDelegate>
#define DEFAULT_GRID_ITEM_MARGIN 11
#define DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT "bottom"
#define DEFAULT_GRID_SPACING 7
#define DEFAULT_GRID_LAYOUT "centered"
class GridItem;
class ThumbnailDelegate : public QStyledItemDelegate class ThumbnailDelegate : public QStyledItemDelegate
{ {
Q_OBJECT Q_OBJECT
public: public:
ThumbnailDelegate(QObject* parent = 0); ThumbnailDelegate(const GridItem &gridItem, QObject* parent = 0);
void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const; void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const;
private:
const GridItem &m_style;
}; };
class GridView : public QAbstractItemView class GridView : public QAbstractItemView
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString layout READ getLayout WRITE setLayout DESIGNABLE true SCRIPTABLE true)
Q_PROPERTY(int spacing READ getSpacing WRITE setSpacing DESIGNABLE true SCRIPTABLE true)
public: public:
enum ViewMode enum ViewMode
{ {
@ -35,6 +48,10 @@ public:
void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint); void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint);
void setGridSize(const int newSize); void setGridSize(const int newSize);
void setviewMode(ViewMode mode); void setviewMode(ViewMode mode);
QString getLayout() const;
void setLayout(QString layout);
int getSpacing() const;
void setSpacing(const int spacing);
signals: signals:
void visibleItemsChangedMaybe() const; void visibleItemsChangedMaybe() const;
@ -63,7 +80,7 @@ private:
void refresh(); void refresh();
int m_size = 255; int m_size = 255;
int m_spacing = 7; int m_spacing = DEFAULT_GRID_SPACING;
QVector<QModelIndex> m_visibleIndexes; QVector<QModelIndex> m_visibleIndexes;
ViewMode m_viewMode = Centered; ViewMode m_viewMode = Centered;
mutable int m_idealHeight; mutable int m_idealHeight;

View File

@ -431,5 +431,9 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral(R"(
GridView { GridView {
background-color:rgb(25,25,25); background-color:rgb(25,25,25);
selection-color: white; selection-color: white;
qproperty-layout: "fixed";
}
GridItem {
qproperty-thumbnailvalign: "center";
} }
)"); )");

View File

@ -322,6 +322,7 @@ MainWindow::MainWindow(QWidget *parent) :
,m_playlistThumbnailDownloadWasCanceled(false) ,m_playlistThumbnailDownloadWasCanceled(false)
,m_pendingDirScrollPath() ,m_pendingDirScrollPath()
,m_thumbnailTimer(new QTimer(this)) ,m_thumbnailTimer(new QTimer(this))
,m_gridItem(this)
{ {
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
QDir playlistDir(settings->paths.directory_playlist); QDir playlistDir(settings->paths.directory_playlist);
@ -417,7 +418,7 @@ MainWindow::MainWindow(QWidget *parent) :
m_tableView->setSortingEnabled(true); m_tableView->setSortingEnabled(true);
m_tableView->verticalHeader()->setVisible(false); m_tableView->verticalHeader()->setVisible(false);
m_gridView->setItemDelegate(new ThumbnailDelegate(this)); m_gridView->setItemDelegate(new ThumbnailDelegate(m_gridItem, this));
m_gridView->setModel(m_proxyModel); m_gridView->setModel(m_proxyModel);
m_logWidget->setObjectName("logWidget"); m_logWidget->setObjectName("logWidget");
@ -566,6 +567,7 @@ MainWindow::MainWindow(QWidget *parent) :
connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems())); connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems()));
connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems())); connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems()));
/* TODO: Handle scroll and resize differently. */
connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer())); connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer()));
connect(m_gridView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&))); connect(m_gridView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&)));
@ -1134,6 +1136,8 @@ void MainWindow::setTheme(Theme theme)
{ {
m_currentTheme = theme; m_currentTheme = theme;
setDefaultCustomProperties();
switch(theme) switch(theme)
{ {
case THEME_SYSTEM_DEFAULT: case THEME_SYSTEM_DEFAULT:
@ -1159,6 +1163,14 @@ void MainWindow::setTheme(Theme theme)
} }
} }
void MainWindow::setDefaultCustomProperties()
{
m_gridView->setLayout(QString(DEFAULT_GRID_LAYOUT));
m_gridView->setSpacing(DEFAULT_GRID_SPACING);
m_gridItem.setThumbnailVerticalAlign(QString(DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT));
m_gridItem.setPadding(DEFAULT_GRID_ITEM_MARGIN);
}
void MainWindow::changeThumbnailType(ThumbnailType type) void MainWindow::changeThumbnailType(ThumbnailType type)
{ {
m_playlistModel->setThumbnailType(type); m_playlistModel->setThumbnailType(type);

View File

@ -271,6 +271,26 @@ public slots:
void appendMessage(const QString& text); void appendMessage(const QString& text);
}; };
/* Used to store styling since delegates don't inherit QWidget. */
class GridItem : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString thumbnailvalign READ getThumbnailVerticalAlign WRITE setThumbnailVerticalAlign)
Q_PROPERTY(int padding READ getPadding WRITE setPadding)
public:
GridItem(QWidget* parent);
Qt::AlignmentFlag thumbnailVerticalAlignmentFlag;
int padding;
int getPadding() const;
void setPadding(const int value);
QString getThumbnailVerticalAlign() const;
void setThumbnailVerticalAlign(const QString valign);
};
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
@ -347,6 +367,7 @@ public:
QString getSpecialPlaylistPath(SpecialPlaylist playlist); QString getSpecialPlaylistPath(SpecialPlaylist playlist);
QVector<QPair<QString, QString> > getPlaylists(); QVector<QPair<QString, QString> > getPlaylists();
QString getScrubbedString(QString str); QString getScrubbedString(QString str);
void setDefaultCustomProperties();
signals: signals:
void thumbnailChanged(const QPixmap &pixmap); void thumbnailChanged(const QPixmap &pixmap);
@ -574,6 +595,7 @@ private:
QString m_pendingDirScrollPath; QString m_pendingDirScrollPath;
QTimer *m_thumbnailTimer; QTimer *m_thumbnailTimer;
GridItem m_gridItem;
protected: protected:
void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event);