Commit bc778682 authored by Jakob Leben's avatar Jakob Leben

Qt: add a list view

parent 01de7da1
......@@ -23,6 +23,7 @@
#include "components/playlist/icon_view.hpp"
#include "components/playlist/playlist_model.hpp"
#include "components/playlist/sorting.h"
#include "input_manager.hpp"
#include <QApplication>
......@@ -41,141 +42,213 @@
#define ITEMS_SPACING 10
#define ART_RADIUS 5
static const QRect drawRect = QRect( 0, 0, RECT_SIZE_W, RECT_SIZE_H );
static const QRect artRect = drawRect.adjusted( OFFSET - 1, 2, - OFFSET, - OFFSET *2 );
void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
QString AbstractPlViewItemDelegate::getMeta( const QModelIndex & index, int meta ) const
{
PLItem *currentItem = static_cast<PLItem*>( index.internalPointer() );
assert( currentItem );
return index.model()->index( index.row(),
PLModel::columnFromMeta( meta ),
index.parent() )
.data().toString();
}
char *meta = input_item_GetTitleFbName( currentItem->inputItem() );
QString title = qfu( meta );
free( meta );
void AbstractPlViewItemDelegate::paintPlayingItemBg( QPainter *painter, const QStyleOptionViewItem & option ) const
{
painter->save();
painter->setOpacity( 0.5 );
painter->setBrush( QBrush( Qt::gray ) );
painter->fillRect( option.rect, option.palette.color( QPalette::Dark ) );
painter->restore();
}
meta = input_item_GetArtist( currentItem->inputItem() );
QString artist = qfu( meta );
free( meta );
QPixmap AbstractPlViewItemDelegate::getArtPixmap( const QModelIndex & index, const QSize & size ) const
{
PLItem *item = static_cast<PLItem*>( index.internalPointer() );
assert( item );
/* Primary ArtUrl */
QString artUrl = InputManager::decodeArtURL( currentItem->inputItem() );
QString artUrl = InputManager::decodeArtURL( item->inputItem() );
/* else look up through all children and use the first picture found */
if( artUrl.isEmpty() )
{
for( int i = 0; i < currentItem->childCount(); i++ )
for( int i = 0; i < item->childCount(); i++ )
{
artUrl = InputManager::decodeArtURL( currentItem->child( i )->inputItem() );
artUrl = InputManager::decodeArtURL( item->child( i )->inputItem() );
if( !artUrl.isEmpty() )
break;
}
}
/*if( option.state & QStyle::State_Selected )
painter->fillRect(option.rect, option.palette.highlight());*/
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
painter );
QPixmap artPix( size );
/* Pixmap where all the rendering will happen and that will be cached */
QPixmap pix;
QString key = artUrl + QString("%1%2").arg(size.width()).arg(size.height());
QString key = title + artist + artUrl
+ QString( index.data( PLModel::IsCurrentRole ).toBool() );
if(QPixmapCache::find( key, pix ))
if( !QPixmapCache::find( key, artPix ))
{
// cool, we found it in the cache
painter->drawPixmap( option.rect, pix );
return;
QPixmap tmp;
bool cache = false;
if( artUrl.isEmpty() || !tmp.load( artUrl ) )
{
tmp = QPixmap( ":/noart64" ).scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
}
else
{
tmp = tmp.scaled( size, Qt::KeepAspectRatio, Qt::SmoothTransformation );
cache = true;
}
artPix.fill( Qt::black );
QPainter p( &artPix );
p.drawPixmap( (size.width() - tmp.width()) / 2,
(size.height() - tmp.height()) / 2,
tmp );
if( cache ) QPixmapCache::insert( key, artPix );
}
return artPix;
}
void PlIconViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QString title = getMeta( index, COLUMN_TITLE );
QString artist = getMeta( index, COLUMN_ARTIST );
/* Background decorations */
pix = QPixmap( RECT_SIZE_W, RECT_SIZE_H );
pix.fill( Qt::transparent );
QPixmap artPix = getArtPixmap( index, QSize( ART_SIZE, ART_SIZE ) );
QPainter *pixpainter = new QPainter( &pix );
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option,
painter );
pixpainter->setRenderHints(
QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
QPainter::TextAntialiasing );
painter->save();
if( index.data( PLModel::IsCurrentRole ).toBool() )
{
pixpainter->save();
pixpainter->setOpacity( 0.2 );
pixpainter->setBrush( QBrush( Qt::gray ) );
pixpainter->drawRoundedRect( 0, 0, RECT_SIZE_W, RECT_SIZE_H, ART_RADIUS, ART_RADIUS );
pixpainter->restore();
painter->save();
painter->setOpacity( 0.2 );
painter->setBrush( QBrush( Qt::gray ) );
painter->drawRoundedRect( option.rect.adjusted( 0, 0, -1, -1 ), ART_RADIUS, ART_RADIUS );
painter->restore();
}
// Draw the drop shadow
pixpainter->save();
pixpainter->setOpacity( 0.7 );
pixpainter->setBrush( QBrush( Qt::gray ) );
pixpainter->drawRoundedRect( artRect.adjusted( 2, 2, 2, 2 ), ART_RADIUS, ART_RADIUS );
pixpainter->restore();
QRect artRect = option.rect.adjusted( OFFSET - 1, 2, - OFFSET, - OFFSET *2 );
// load album art in the pixmap
QPixmap artPix;
if( artUrl.isEmpty() || !artPix.load( artUrl ) )
artPix = QPixmap( ":/noart64" );
else
artPix = artPix.scaled( ART_SIZE, ART_SIZE,
Qt::KeepAspectRatioByExpanding );
// Draw the drop shadow
painter->save();
painter->setOpacity( 0.7 );
painter->setBrush( QBrush( Qt::darkGray ) );
painter->setPen( Qt::NoPen );
painter->drawRoundedRect( artRect.adjusted( 0, 0, 2, 2 ), ART_RADIUS, ART_RADIUS );
painter->restore();
// Draw the art pixmap
QPainterPath artRectPath;
artRectPath.addRoundedRect( artRect, ART_RADIUS, ART_RADIUS );
pixpainter->setClipPath( artRectPath );
pixpainter->drawPixmap( artRect, artPix );
pixpainter->setClipping( false );
painter->setClipPath( artRectPath );
painter->drawPixmap( artRect, artPix );
painter->setClipping( false );
/* */
QColor text = qApp->palette().text().color();
if( option.state & QStyle::State_Selected )
painter->setPen( option.palette.color( QPalette::HighlightedText ) );
// Draw title
pixpainter->setPen( text );
QFont font;
font.setPointSize( 7 );
font.setItalic( true );
font.setBold( index.data( Qt::FontRole ).value<QFont>().bold() );
pixpainter->setFont( font );
QFontMetrics fm = pixpainter->fontMetrics();
QRect textRect = drawRect.adjusted( 1, ART_SIZE + 4, 0, -1 );
// Draw title
font.setItalic( true );
painter->setFont( font );
QFontMetrics fm = painter->fontMetrics();
QRect textRect = option.rect.adjusted( 1, ART_SIZE + 8, 0, -1 );
textRect.setHeight( fm.height() + 1 );
pixpainter->drawText( textRect,
painter->drawText( textRect,
fm.elidedText( title, Qt::ElideRight, textRect.width() ),
QTextOption( Qt::AlignCenter ) );
// Draw artist
pixpainter->setPen( text.lighter( 220 ) );
painter->setPen( painter->pen().color().lighter( 150 ) );
font.setItalic( false );
pixpainter->setFont( font );
fm = pixpainter->fontMetrics();
painter->setFont( font );
fm = painter->fontMetrics();
textRect = textRect.adjusted( 0, textRect.height(),
0, textRect.height() );
pixpainter->drawText( textRect,
painter->drawText( textRect,
fm.elidedText( artist, Qt::ElideRight, textRect.width() ),
QTextOption( Qt::AlignCenter ) );
delete pixpainter; // Ensure all paint operations have finished
painter->restore();
}
// Here real drawing happens
painter->drawPixmap( option.rect, pix );
QSize PlIconViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
return QSize( RECT_SIZE_W, RECT_SIZE_H );
}
// Cache the rendering
QPixmapCache::insert( key, pix );
#define LISTVIEW_ART_SIZE 45
void PlListViewItemDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
QModelIndex parent = index.parent();
QModelIndex i;
QString title = getMeta( index, COLUMN_TITLE );
QString duration = getMeta( index, COLUMN_DURATION );
if( !duration.isEmpty() ) title += QString(" [%1]").arg( duration );
QString artist = getMeta( index, COLUMN_ARTIST );
QString album = getMeta( index, COLUMN_ALBUM );
QString trackNum = getMeta( index, COLUMN_TRACK_NUMBER );
QString artistAlbum = artist
+ ( artist.isEmpty() ? QString() : QString( ": " ) )
+ album
+ ( album.isEmpty() || trackNum.isEmpty() ?
QString() : QString( " [#%1]" ).arg( trackNum ) );
QPixmap artPix = getArtPixmap( index, QSize( LISTVIEW_ART_SIZE, LISTVIEW_ART_SIZE ) );
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &option, painter );
if( index.data( PLModel::IsCurrentRole ).toBool() )
paintPlayingItemBg( painter, option );
painter->drawPixmap( option.rect.topLeft() + QPoint(3,3), artPix );
int textH = option.fontMetrics.height() + 2;
int margin = ( option.rect.height() / 2 ) - textH;
QRect textRect = option.rect.adjusted( LISTVIEW_ART_SIZE + 10,
margin,
-10,
margin * -1 - ( artistAlbum.isEmpty() ? 0 : textH ) );
painter->save();
if( option.state & QStyle::State_Selected )
painter->setPen( option.palette.color( QPalette::HighlightedText ) );
QTextOption textOpt( Qt::AlignVCenter | Qt::AlignLeft );
textOpt.setWrapMode( QTextOption::NoWrap );
QFont f( option.font );
if( index.data( PLModel::IsCurrentRole ).toBool() ) f.setBold( true );
f.setItalic( true );
painter->setFont( f );
painter->drawText( textRect, title, textOpt );
f.setItalic( false );
painter->setFont( f );
textRect.moveTop( textRect.top() + textH );
painter->drawText( textRect, artistAlbum, textOpt );
painter->restore();
}
QSize PlListViewItemDelegate::sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const
{
return QSize( RECT_SIZE_W, RECT_SIZE_H );
return QSize( LISTVIEW_ART_SIZE + 6, LISTVIEW_ART_SIZE + 6 );
}
PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
{
setModel( model );
......@@ -183,12 +256,24 @@ PlIconView::PlIconView( PLModel *model, QWidget *parent ) : QListView( parent )
setMovement( QListView::Static );
setResizeMode( QListView::Adjust );
setGridSize( QSize( RECT_SIZE_W, RECT_SIZE_H ) );
setUniformItemSizes( true );
setWrapping( true );
setUniformItemSizes( true );
setSelectionMode( QAbstractItemView::ExtendedSelection );
setAcceptDrops( true );
PlListViewItemDelegate *pl = new PlListViewItemDelegate( this );
setItemDelegate( pl );
PlIconViewItemDelegate *delegate = new PlIconViewItemDelegate( this );
setItemDelegate( delegate );
}
PlListView::PlListView( PLModel *model, QWidget *parent ) : QListView( parent )
{
setModel( model );
setViewMode( QListView::ListMode );
setUniformItemSizes( true );
setSelectionMode( QAbstractItemView::ExtendedSelection );
setAcceptDrops( true );
setAlternatingRowColors( true );
PlListViewItemDelegate *delegate = new PlListViewItemDelegate( this );
setItemDelegate( delegate );
}
......@@ -30,12 +30,32 @@
class QPainter;
class PLModel;
class PlListViewItemDelegate : public QStyledItemDelegate
class AbstractPlViewItemDelegate : public QStyledItemDelegate
{
public:
AbstractPlViewItemDelegate( QWidget * parent = 0 ) : QStyledItemDelegate(parent) {}
QString getMeta( const QModelIndex & index, int meta ) const;
void paintPlayingItemBg( QPainter *painter, const QStyleOptionViewItem & option ) const;
QPixmap getArtPixmap( const QModelIndex & index, const QSize & size ) const;
};
class PlIconViewItemDelegate : public AbstractPlViewItemDelegate
{
Q_OBJECT
public:
PlListViewItemDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}
PlIconViewItemDelegate(QWidget *parent = 0) : AbstractPlViewItemDelegate(parent) {}
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
};
class PlListViewItemDelegate : public AbstractPlViewItemDelegate
{
Q_OBJECT
public:
PlListViewItemDelegate(QWidget *parent = 0) : AbstractPlViewItemDelegate(parent) {}
void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const;
......@@ -49,5 +69,13 @@ public:
PlIconView( PLModel *model, QWidget *parent = 0 );
};
class PlListView : public QListView
{
Q_OBJECT
public:
PlListView( PLModel *model, QWidget *parent = 0 );
};
#endif
......@@ -339,7 +339,12 @@ QVariant PLModel::data( const QModelIndex &index, int role ) const
QFont f; f.setBold( true ); return QVariant( f );
}
}
else if( role == Qt::BackgroundRole && isCurrent( index ) )
{
return QVariant( QBrush( Qt::gray ) );
}
else if( role == IsCurrentRole ) return QVariant( isCurrent( index ) );
return QVariant();
}
......@@ -532,7 +537,7 @@ PLItem * PLModel::findInner( PLItem *root, int i_id, bool b_input )
#undef CACHE
#undef ICACHE
int PLModel::columnToMeta( int _column ) const
int PLModel::columnToMeta( int _column )
{
int meta = 1;
int column = 0;
......@@ -546,7 +551,7 @@ int PLModel::columnToMeta( int _column ) const
return meta;
}
int PLModel::columnFromMeta( int meta_col ) const
int PLModel::columnFromMeta( int meta_col )
{
int meta = 1;
int column = 0;
......
......@@ -90,6 +90,8 @@ public:
QModelIndex currentIndex();
bool isCurrent( const QModelIndex &index ) const;
int itemId( const QModelIndex &index ) const;
static int columnFromMeta( int meta_column );
static int columnToMeta( int column );
/* Actions */
void popup( const QModelIndex & index, const QPoint &point, const QModelIndexList &list );
......@@ -147,8 +149,6 @@ private:
PLItem *findById( PLItem *, int );
PLItem *findByInput( PLItem *, int );
PLItem *findInner( PLItem *, int , bool );
int columnFromMeta( int meta_column ) const;
int columnToMeta( int column ) const;
bool canEdit() const;
PLItem *p_cached_item;
......
......@@ -73,6 +73,7 @@ static inline char * psz_column_meta( input_item_t *p_item, uint32_t i_column )
return input_item_GetTitleFbName( p_item );
case COLUMN_DURATION:
i_duration = input_item_GetDuration( p_item ) / 1000000;
if( i_duration == 0 ) return NULL;
secstotimestr( psz_duration, i_duration );
return strdup( psz_duration );
case COLUMN_ARTIST:
......
......@@ -52,6 +52,10 @@
#include "sorting.h"
static const QString viewNames[] = { qtr( "Detailed View" ),
qtr( "Icon View" ),
qtr( "List View" ) };
StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
intf_thread_t *_p_intf,
playlist_t *p_playlist,
......@@ -64,6 +68,7 @@ StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
iconView = NULL;
treeView = NULL;
listView = NULL;
model = new PLModel( p_playlist, p_intf, p_root, this );
currentRootId = -1;
......@@ -101,15 +106,13 @@ StandardPLPanel::StandardPLPanel( PlaylistWidget *_parent,
QActionGroup *actionGroup = new QActionGroup( this );
treeViewAction = actionGroup->addAction( "Detailed view" );
treeViewAction->setCheckable( true );
viewSelectionMapper->setMapping( treeViewAction, TREE_VIEW );
CONNECT( treeViewAction, triggered(), viewSelectionMapper, map() );
iconViewAction = actionGroup->addAction( "Icon view" );
iconViewAction->setCheckable( true );
viewSelectionMapper->setMapping( iconViewAction, ICON_VIEW );
CONNECT( iconViewAction, triggered(), viewSelectionMapper, map() );
for( int i = 0; i < VIEW_COUNT; i++ )
{
viewActions[i] = actionGroup->addAction( viewNames[i] );
viewActions[i]->setCheckable( true );
viewSelectionMapper->setMapping( viewActions[i], i );
CONNECT( viewActions[i], triggered(), viewSelectionMapper, map() );
}
BUTTONACT( viewButton, cycleViews() );
QMenu *viewMenu = new QMenu( this );
......@@ -138,7 +141,12 @@ StandardPLPanel::~StandardPLPanel()
getSettings()->beginGroup("Playlist");
if( treeView )
getSettings()->setValue( "headerStateV2", treeView->header()->saveState() );
getSettings()->setValue( "view-mode", ( currentView == iconView ) ? ICON_VIEW : TREE_VIEW );
if( currentView == treeView )
getSettings()->setValue( "view-mode", TREE_VIEW );
else if( currentView == listView )
getSettings()->setValue( "view-mode", LIST_VIEW );
else if( currentView == iconView )
getSettings()->setValue( "view-mode", ICON_VIEW );
getSettings()->endGroup();
}
......@@ -287,6 +295,21 @@ void StandardPLPanel::createIconView()
layout->addWidget( iconView, 1, 0, 1, -1 );
}
void StandardPLPanel::createListView()
{
listView = new PlListView( model, this );
listView->setContextMenuPolicy( Qt::CustomContextMenu );
CONNECT( listView, customContextMenuRequested( const QPoint & ),
this, popupPlView( const QPoint & ) );
CONNECT( listView, activated( const QModelIndex & ),
this, activate( const QModelIndex & ) );
CONNECT( locationBar, invoked( const QModelIndex & ),
listView, setRootIndex( const QModelIndex & ) );
layout->addWidget( listView, 1, 0, 1, -1 );
}
void StandardPLPanel::createTreeView()
{
/* Create and configure the QTreeView */
......@@ -354,9 +377,10 @@ void StandardPLPanel::showView( int i_view )
createTreeView();
locationBar->setIndex( treeView->rootIndex() );
if( iconView ) iconView->hide();
if( listView ) listView->hide();
treeView->show();
currentView = treeView;
treeViewAction->setChecked( true );
viewActions[i_view]->setChecked( true );
break;
}
case ICON_VIEW:
......@@ -364,11 +388,31 @@ void StandardPLPanel::showView( int i_view )
if( iconView == NULL )
createIconView();
locationBar->setIndex( iconView->rootIndex() );
if( treeView ) treeView->hide();
if( listView ) {
listView->hide();
iconView->setRootIndex( listView->rootIndex() );
}
locationBar->setIndex( iconView->rootIndex() );
iconView->show();
currentView = iconView;
iconViewAction->setChecked( true );
viewActions[i_view]->setChecked( true );
break;
}
case LIST_VIEW:
{
if( listView == NULL )
createListView();
if( treeView ) treeView->hide();
if( iconView ) {
iconView->hide();
listView->setRootIndex( iconView->rootIndex() );
}
locationBar->setIndex( listView->rootIndex() );
listView->show();
currentView = listView;
viewActions[i_view]->setChecked( true );
break;
}
default:;
......@@ -380,6 +424,8 @@ void StandardPLPanel::cycleViews()
if( currentView == iconView )
showView( TREE_VIEW );
else if( currentView == treeView )
showView( LIST_VIEW );
else if( currentView == listView )
showView( ICON_VIEW );
else
assert( 0 );
......@@ -395,8 +441,8 @@ void StandardPLPanel::activate( const QModelIndex &index )
{
if( model->hasChildren( index ) )
{
if( currentView == iconView ) {
iconView->setRootIndex( index );
if( currentView == iconView || currentView == listView ) {
currentView->setRootIndex( index );
locationBar->setIndex( index );
}
}
......@@ -429,8 +475,8 @@ void StandardPLPanel::browseInto( input_item_t *p_input )
playlist_Unlock( THEPL );
if( currentView == iconView ) {
iconView->setRootIndex( index );
if( currentView == iconView || currentView == listView ) {
currentView->setRootIndex( index );
locationBar->setIndex( index );
}
else
......
......@@ -46,6 +46,7 @@ class QPushButton;
class QKeyEvent;
class QWheelEvent;
class PlIconView;
class PlListView;
class LocationBar;
class StandardPLPanel: public QWidget
......@@ -64,6 +65,14 @@ protected:
PLModel *model;
private:
enum {
TREE_VIEW = 0,
ICON_VIEW,
LIST_VIEW,
VIEW_COUNT
};
intf_thread_t *p_intf;
QWidget *parent;
......@@ -74,8 +83,10 @@ private:
QTreeView *treeView;
PlIconView *iconView;
PlListView *listView;
QAbstractItemView *currentView;
QAction *viewActions[ VIEW_COUNT ];
QAction *iconViewAction, *treeViewAction;
int currentRootId;
QSignalMapper *selectColumnsSigMapper;
......@@ -83,14 +94,9 @@ private:
int last_activated_id;
enum {
TREE_VIEW = 0,
ICON_VIEW,
COVER_VIEW,
};
void createTreeView();
void createIconView();
void createListView();
public slots:
virtual void setRoot( playlist_item_t * );
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment