Merge pull request #7737 from CozmoP/modelview

Qt: custom playlist model and playlist view
This commit is contained in:
Twinaphex 2019-01-06 16:07:05 +01:00 committed by GitHub
commit ad3e66dcb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1247 additions and 1232 deletions

View File

@ -340,7 +340,7 @@ ifeq ($(HAVE_QT), 1)
ui/drivers/qt/ui_qt_browser_window.o \
ui/drivers/qt/ui_qt_load_core_window.o \
ui/drivers/qt/ui_qt_msg_window.o \
ui/drivers/qt/flowlayout.o \
ui/drivers/qt/gridview.o \
ui/drivers/qt/shaderparamsdialog.o \
ui/drivers/qt/coreoptionsdialog.o \
ui/drivers/qt/filedropwidget.o \
@ -355,7 +355,7 @@ ifeq ($(HAVE_QT), 1)
MOC_HEADERS += ui/drivers/ui_qt.h \
ui/drivers/qt/ui_qt_load_core_window.h \
ui/drivers/qt/flowlayout.h \
ui/drivers/qt/gridview.h \
ui/drivers/qt/shaderparamsdialog.h \
ui/drivers/qt/coreoptionsdialog.h \
ui/drivers/qt/filedropwidget.h \

View File

@ -43,7 +43,7 @@ UI
#include "../ui/drivers/qt/ui_qt_browser_window.cpp"
#include "../ui/drivers/qt/ui_qt_msg_window.cpp"
#include "../ui/drivers/qt/ui_qt_application.cpp"
#include "../ui/drivers/qt/flowlayout.cpp"
#include "../ui/drivers/qt/gridview.cpp"
#include "../ui/drivers/qt/shaderparamsdialog.cpp"
#include "../ui/drivers/qt/coreoptionsdialog.cpp"
#include "../ui/drivers/qt/filedropwidget.cpp"
@ -59,7 +59,7 @@ UI
#include "../ui/drivers/qt/moc_coreinfodialog.cpp"
#include "../ui/drivers/qt/moc_coreoptionsdialog.cpp"
#include "../ui/drivers/qt/moc_filedropwidget.cpp"
#include "../ui/drivers/qt/moc_flowlayout.cpp"
#include "../ui/drivers/qt/moc_gridview.cpp"
#include "../ui/drivers/qt/moc_playlistentrydialog.cpp"
#include "../ui/drivers/qt/moc_shaderparamsdialog.cpp"
#include "../ui/drivers/qt/moc_ui_qt_load_core_window.cpp"

View File

@ -7679,6 +7679,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST,
"Start on playlist:"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE,
"Icon view thumbnail type:"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT,
"Thumbnail cache limit:"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS,
"Download All Thumbnails"

View File

@ -1988,6 +1988,8 @@ enum msg_hash_enums
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT,
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT,
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST,
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE,
MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT,
MENU_ENUM_LABEL_VALUE_QT_MENU_TOOLS,
MENU_ENUM_LABEL_VALUE_QT_MENU_HELP,
MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER,

View File

@ -41,7 +41,12 @@ void FileDropWidget::paintEvent(QPaintEvent *event)
void FileDropWidget::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Delete)
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
{
event->accept();
emit enterPressed();
}
else if (event->key() == Qt::Key_Delete)
{
event->accept();
emit deletePressed();

View File

@ -15,6 +15,7 @@ public:
FileDropWidget(QWidget *parent = 0);
signals:
void filesDropped(QStringList files);
void enterPressed();
void deletePressed();
protected:
void dragEnterEvent(QDragEnterEvent *event);

View File

@ -1,248 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
/* Original work Copyright (C) 2016 The Qt Company Ltd.
* Modified work Copyright (C) 2018 - Brad Parker
*/
#include <QtWidgets>
#include "flowlayout.h"
#include "../ui_qt.h"
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
connect(this, SIGNAL(signalAddWidgetDeferred(QPointer<ThumbnailWidget>)), this, SLOT(onAddWidgetDeferred(QPointer<ThumbnailWidget>)), Qt::QueuedConnection);
}
FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
: m_hSpace(hSpacing), m_vSpace(vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
FlowLayout::~FlowLayout()
{
QLayoutItem *item = NULL;
while ((item = takeAt(0)) != NULL)
delete item;
}
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0)
return m_hSpace;
else
return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
}
int FlowLayout::verticalSpacing() const
{
if (m_vSpace >= 0)
return m_vSpace;
else
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
int FlowLayout::count() const
{
return itemList.size();
}
QLayoutItem* FlowLayout::itemAt(int index) const
{
return itemList.value(index);
}
QLayoutItem* FlowLayout::takeAt(int index)
{
if (index >= 0 && index < itemList.size())
return itemList.takeAt(index);
else
return NULL;
}
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
bool FlowLayout::hasHeightForWidth() const
{
return true;
}
int FlowLayout::heightForWidth(int width) const
{
int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
doLayout(rect, false);
}
QSize FlowLayout::sizeHint() const
{
return minimumSize();
}
QSize FlowLayout::minimumSize() const
{
QSize size;
int i = 0;
if (itemList.isEmpty())
return size;
for (i = 0; i < itemList.count(); i++)
{
const QLayoutItem *item = itemList.at(i);
size = size.expandedTo(item->minimumSize());
}
size += QSize(2 * margin(), 2 * margin());
return size;
}
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
QRect effectiveRect;
int left = 0, top = 0, right = 0, bottom = 0;
int x = 0;
int y = 0;
int lineHeight = 0;
int i = 0;
getContentsMargins(&left, &top, &right, &bottom);
effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
x = effectiveRect.x();
y = effectiveRect.y();
if (itemList.isEmpty())
return y + lineHeight - rect.y() + bottom;
for (i = 0; i < itemList.count(); i++)
{
QLayoutItem *item = itemList.at(i);
const QWidget *wid = item->widget();
int spaceX = horizontalSpacing();
int spaceY = 0;
int nextX = 0;
if (spaceX == -1)
spaceX = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
spaceY = verticalSpacing();
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0)
{
x = effectiveRect.x();
y = y + lineHeight + spaceY;
nextX = x + item->sizeHint().width() + spaceX;
lineHeight = 0;
}
if (!testOnly)
item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
x = nextX;
lineHeight = qMax(lineHeight, item->sizeHint().height());
}
return y + lineHeight - rect.y() + bottom;
}
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
const QObject *parentObj = parent();
if (!parentObj)
return -1;
else if (parentObj->isWidgetType())
{
const QWidget *pw = static_cast<const QWidget*>(parentObj);
return pw->style()->pixelMetric(pm, NULL, pw);
}
else
return static_cast<const QLayout*>(parentObj)->spacing();
}
void FlowLayout::addWidgetDeferred(QPointer<ThumbnailWidget> widget)
{
emit signalAddWidgetDeferred(widget);
}
void FlowLayout::onAddWidgetDeferred(QPointer<ThumbnailWidget> widget)
{
/* widget might have been deleted before we got to it since this uses a queued connection, hence the guarded QPointer */
if (!widget)
return;
addWidget(widget);
}

View File

@ -1,105 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
/* Original work Copyright (C) 2016 The Qt Company Ltd.
* Modified work Copyright (C) 2018 - Brad Parker
*/
/* bparker: Removed C++11 override keyword from original source
* Changed QList to QVector
*/
#ifndef FLOWLAYOUT_H
#define FLOWLAYOUT_H
#include <QLayout>
#include <QRect>
#include <QStyle>
class ThumbnailWidget;
class FlowLayout : public QLayout
{
Q_OBJECT
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);
~FlowLayout();
void addItem(QLayoutItem *item);
int horizontalSpacing() const;
int verticalSpacing() const;
Qt::Orientations expandingDirections() const;
bool hasHeightForWidth() const;
int heightForWidth(int) const;
int count() const;
QLayoutItem* itemAt(int index) const;
QSize minimumSize() const;
void setGeometry(const QRect &rect);
QSize sizeHint() const;
QLayoutItem* takeAt(int index);
void addWidgetDeferred(QPointer<ThumbnailWidget> widget);
signals:
void signalAddWidgetDeferred(QPointer<ThumbnailWidget> widget);
private slots:
void onAddWidgetDeferred(QPointer<ThumbnailWidget> widget);
private:
int doLayout(const QRect &rect, bool testOnly) const;
int smartSpacing(QStyle::PixelMetric pm) const;
QVector<QLayoutItem*> itemList;
int m_hSpace;
int m_vSpace;
};
#endif // FLOWLAYOUT_H

481
ui/drivers/qt/gridview.cpp Normal file
View File

@ -0,0 +1,481 @@
#include <QScrollBar>
#include <QPainter>
#include "gridview.h"
#include "../ui_qt.h"
/* http://www.informit.com/articles/article.aspx?p=1613548 */
ThumbnailDelegate::ThumbnailDelegate(const GridItem &gridItem, QObject* parent) :
QStyledItemDelegate(parent), m_style(gridItem)
{
}
void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const
{
QStyleOptionViewItem opt = option;
const QWidget *widget = opt.widget;
QStyle *style = widget->style();
int padding = m_style.padding;
int textTopMargin = 4; /* Qt seemingly reports -4 the actual line height. */
int textHeight = painter->fontMetrics().height() + padding + padding;
QRect rect = opt.rect;
QRect adjusted = rect.adjusted(padding, padding, -padding, -textHeight + textTopMargin);
QPixmap pixmap = index.data(PlaylistModel::THUMBNAIL).value<QPixmap>();
painter->save();
initStyleOption(&opt, index);
/* draw the background */
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget);
/* draw the image */
if (!pixmap.isNull())
{
QPixmap pixmapScaled = pixmap.scaled(adjusted.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | m_style.thumbnailVerticalAlignmentFlag, pixmapScaled);
}
/* draw the text */
if (!opt.text.isEmpty())
{
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
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);
if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active))
cg = QPalette::Inactive;
if (opt.state & QStyle::State_Selected)
painter->setPen(opt.palette.color(cg, QPalette::HighlightedText));
else
painter->setPen(opt.palette.color(cg, QPalette::Text));
painter->setFont(opt.font);
painter->drawText(textRect, Qt::AlignCenter, elidedText);
}
painter->restore();
}
GridView::GridView(QWidget *parent) : QAbstractItemView(parent), m_idealHeight(0), m_hashIsDirty(false)
{
setFocusPolicy(Qt::WheelFocus);
horizontalScrollBar()->setRange(0, 0);
verticalScrollBar()->setRange(0, 0);
}
void GridView::setModel(QAbstractItemModel *newModel)
{
if (model())
disconnect(model(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(rowsRemoved(QModelIndex, int, int)));
QAbstractItemView::setModel(newModel);
connect(newModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(rowsRemoved(QModelIndex, int, int)));
m_hashIsDirty = true;
}
void GridView::setviewMode(ViewMode mode)
{
m_viewMode = mode;
}
void GridView::calculateRectsIfNecessary() const
{
if (!m_hashIsDirty)
return;
int x = m_spacing;
int y = m_spacing;
int row;
int nextX;
const int maxWidth = viewport()->width();
switch (m_viewMode)
{
case Anchored:
{
int columns = (maxWidth - m_spacing) / (m_size + m_spacing);
if (columns > 0)
{
const int actualSpacing = (maxWidth - m_spacing - m_size - (columns - 1) * m_size) / columns;
for (row = 0; row < model()->rowCount(); ++row)
{
nextX = x + m_size + actualSpacing;
if (nextX > maxWidth)
{
x = m_spacing;
y += m_size + m_spacing;
nextX = x + m_size + actualSpacing;
}
m_rectForRow[row] = QRectF(x, y, m_size, m_size);
x = nextX;
}
}
break;
}
case Centered:
{
int columns = (maxWidth - m_spacing) / (m_size + m_spacing);
if (columns > 0)
{
const int actualSpacing = (maxWidth - columns * m_size) / (columns + 1);
x = actualSpacing;
for (row = 0; row < model()->rowCount(); ++row)
{
nextX = x + m_size + actualSpacing;
if (nextX > maxWidth)
{
x = actualSpacing;
y += m_size + m_spacing;
nextX = x + m_size + actualSpacing;
}
m_rectForRow[row] = QRectF(x, y, m_size, m_size);
x = nextX;
}
}
break;
}
case Simple:
for (row = 0; row < model()->rowCount(); ++row)
{
nextX = x + m_size + m_spacing;
if (nextX > maxWidth)
{
x = m_spacing;
y += m_size + m_spacing;
nextX = x + m_size + m_spacing;
}
m_rectForRow[row] = QRectF(x, y, m_size, m_size);
x = nextX;
}
break;
}
m_idealHeight = y + m_size + m_spacing;
m_hashIsDirty = false;
viewport()->update();
}
QRect GridView::visualRect(const QModelIndex &index) const
{
QRect rect;
if (index.isValid())
rect = viewportRectForRow(index.row()).toRect();
return rect;
}
QRectF GridView::viewportRectForRow(int row) const
{
QRectF rect;
calculateRectsIfNecessary();
rect = m_rectForRow.value(row).toRect();
if (!rect.isValid())
return rect;
return QRectF(rect.x() - horizontalScrollBar()->value(), rect.y() - verticalScrollBar()->value(), rect.width(), rect.height());
}
void GridView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint)
{
QRect viewRect = viewport()->rect();
QRect itemRect = visualRect(index);
if (itemRect.left() < viewRect.left())
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + itemRect.left() - viewRect.left());
else if (itemRect.right() > viewRect.right())
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + qMin(itemRect.right() - viewRect.right(), itemRect.left() - viewRect.left()));
if (itemRect.top() < viewRect.top())
verticalScrollBar()->setValue(verticalScrollBar()->value() + itemRect.top() - viewRect.top());
else if (itemRect.bottom() > viewRect.bottom())
verticalScrollBar()->setValue(verticalScrollBar()->value() + qMin(itemRect.bottom() - viewRect.bottom(), itemRect.top() - viewRect.top()));
viewport()->update();
}
/* TODO: Make this more efficient by changing m_rectForRow for another data structure. Look at how Qt's own views do it. */
QModelIndex GridView::indexAt(const QPoint &point_) const
{
QPoint point(point_);
QHash<int, QRectF>::const_iterator i;
point.rx() += horizontalScrollBar()->value();
point.ry() += verticalScrollBar()->value();
calculateRectsIfNecessary();
i = m_rectForRow.constBegin();
while (i != m_rectForRow.constEnd())
{
if (i.value().contains(point))
return model()->index(i.key(), 0, rootIndex());
i++;
}
return QModelIndex();
}
void GridView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
m_hashIsDirty = true;
QAbstractItemView::dataChanged(topLeft, bottomRight);
}
void GridView::refresh()
{
m_hashIsDirty = true;
calculateRectsIfNecessary();
updateGeometries();
}
void GridView::rowsInserted(const QModelIndex &parent, int start, int end)
{
QAbstractItemView::rowsInserted(parent, start, end);
refresh();
}
void GridView::rowsRemoved(const QModelIndex &parent, int start, int end)
{
refresh();
}
void GridView::setGridSize(const int newSize)
{
if (newSize != m_size)
{
m_size = newSize;
refresh();
}
}
void GridView::resizeEvent(QResizeEvent*)
{
refresh();
}
void GridView::reset()
{
m_visibleIndexes.clear();
QAbstractItemView::reset();
refresh();
}
QModelIndex GridView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers)
{
QModelIndex index = currentIndex();
if (index.isValid())
{
if ((cursorAction == MoveLeft && index.row() > 0) || (cursorAction == MoveRight && index.row() + 1 < model()->rowCount()))
{
const int offset = (cursorAction == MoveLeft ? -1 : 1);
index = model()->index(index.row() + offset, index.column(), index.parent());
}
else if ((cursorAction == MoveUp && index.row() > 0) || (cursorAction == MoveDown && index.row() + 1 < model()->rowCount()))
{
const int offset = ((m_size + m_spacing) * (cursorAction == MoveUp ? -1 : 1));
QRect rect = viewportRectForRow(index.row()).toRect();
QPoint point(rect.center().x(), rect.center().y() + offset);
index = indexAt(point);
}
}
return index;
}
int GridView::horizontalOffset() const
{
return horizontalScrollBar()->value();
}
int GridView::verticalOffset() const
{
return verticalScrollBar()->value();
}
void GridView::scrollContentsBy(int dx, int dy)
{
scrollDirtyRegion(dx, dy);
viewport()->scroll(dx, dy);
emit(visibleItemsChangedMaybe());
}
/* TODO: Maybe add a way to get the previous/next visible indexes. */
QVector<QModelIndex> GridView::visibleIndexes() const {
return m_visibleIndexes;
}
void GridView::setSelection(const QRect &rect, QFlags<QItemSelectionModel::SelectionFlag> flags)
{
QRect rectangle;
QHash<int, QRectF>::const_iterator i;
int firstRow = model()->rowCount();
int lastRow = -1;
calculateRectsIfNecessary();
rectangle = rect.translated(horizontalScrollBar()->value(), verticalScrollBar()->value()).normalized();
i = m_rectForRow.constBegin();
while (i != m_rectForRow.constEnd())
{
if (i.value().intersects(rectangle))
{
firstRow = firstRow < i.key() ? firstRow : i.key();
lastRow = lastRow > i.key() ? lastRow : i.key();
}
i++;
}
if (firstRow != model()->rowCount() && lastRow != -1)
{
QItemSelection selection(model()->index(firstRow, 0, rootIndex()), model()->index(lastRow, 0, rootIndex()));
selectionModel()->select(selection, flags);
}
else
{
QModelIndex invalid;
QItemSelection selection(invalid, invalid);
selectionModel()->select(selection, flags);
}
}
QRegion GridView::visualRegionForSelection(const QItemSelection &selection) const
{
QRegion region;
QItemSelectionRange range;
int i;
for (i = 0; i < selection.size(); i++)
{
range = selection.at(i);
int row;
for (row = range.top(); row <= range.bottom(); row++)
{
int column;
for (column = range.left(); column < range.right(); column++)
{
QModelIndex index = model()->index(row, column, rootIndex());
region += visualRect(index);
}
}
}
return region;
}
void GridView::paintEvent(QPaintEvent*)
{
QPainter painter(viewport());
int row;
painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
m_visibleIndexes.clear();
for (row = 0; row < model()->rowCount(rootIndex()); ++row)
{
QModelIndex index = model()->index(row, 0, rootIndex());
QRectF rect = viewportRectForRow(row);
QStyleOptionViewItem option = viewOptions();
if (!rect.isValid() || rect.bottom() < 0 || rect.y() > viewport()->height())
continue;
m_visibleIndexes.append(index);
option.rect = rect.toRect();
if (selectionModel()->isSelected(index))
option.state |= QStyle::State_Selected;
if (currentIndex() == index)
option.state |= QStyle::State_HasFocus;
itemDelegate()->paint(&painter, option, index);
}
}
void GridView::updateGeometries()
{
const int RowHeight = m_size + m_spacing;
QAbstractItemView::updateGeometries();
verticalScrollBar()->setSingleStep(RowHeight);
verticalScrollBar()->setPageStep(viewport()->height());
verticalScrollBar()->setRange(0, qMax(0, m_idealHeight - viewport()->height()));
horizontalScrollBar()->setPageStep(viewport()->width());
horizontalScrollBar()->setRange(0, qMax(0, RowHeight - viewport()->width()));
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;
}

91
ui/drivers/qt/gridview.h Normal file
View File

@ -0,0 +1,91 @@
#ifndef GRIDVIEW_H
#define GRIDVIEW_H
#include <QAbstractItemView>
#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
{
Q_OBJECT
public:
ThumbnailDelegate(const GridItem &gridItem, QObject* parent = 0);
void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const;
private:
const GridItem &m_style;
};
class GridView : public QAbstractItemView
{
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:
enum ViewMode
{
Simple,
Centered,
Anchored
};
GridView(QWidget *parent = 0);
~GridView() {}
QModelIndex indexAt(const QPoint &point_) const;
QVector<QModelIndex> visibleIndexes() const;
QRect visualRect(const QModelIndex &index) const;
void setModel(QAbstractItemModel *model);
void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint);
void setGridSize(const int newSize);
void setviewMode(ViewMode mode);
QString getLayout() const;
void setLayout(QString layout);
int getSpacing() const;
void setSpacing(const int spacing);
signals:
void visibleItemsChangedMaybe() const;
protected slots:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void rowsInserted(const QModelIndex &parent, int start, int end);
void rowsRemoved(const QModelIndex &parent, int start, int end);
void updateGeometries();
void reset();
protected:
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
QRegion visualRegionForSelection(const QItemSelection &selection) const;
bool isIndexHidden(const QModelIndex&) const { return false; }
int horizontalOffset() const;
int verticalOffset() const;
void scrollContentsBy(int dx, int dy);
void setSelection(const QRect &rect, QFlags<QItemSelectionModel::SelectionFlag> flags);
void paintEvent(QPaintEvent*);
void resizeEvent(QResizeEvent*);
private:
QRectF viewportRectForRow(int row) const;
void calculateRectsIfNecessary() const;
void refresh();
int m_size = 255;
int m_spacing = DEFAULT_GRID_SPACING;
QVector<QModelIndex> m_visibleIndexes;
ViewMode m_viewMode = Centered;
mutable int m_idealHeight;
mutable QHash<int, QRectF> m_rectForRow;
mutable bool m_hashIsDirty;
};
#endif

View File

@ -9,9 +9,10 @@
#include <QLayout>
#include <QScreen>
#include <QRegularExpression>
#include <QImageReader>
#include <QtConcurrent>
#include "../ui_qt.h"
#include "flowlayout.h"
#include "playlistentrydialog.h"
extern "C" {
@ -28,6 +29,213 @@ extern "C" {
#include "../../../verbosity.h"
}
PlaylistModel::PlaylistModel(QObject *parent)
: QAbstractListModel(parent)
{
m_imageFormats = QVector<QByteArray>::fromList(QImageReader::supportedImageFormats());
m_fileSanitizerRegex = QRegularExpression("[&*/:`<>?\\|]");
setThumbnailCacheLimit(500);
connect(this, &PlaylistModel::imageLoaded, this, &PlaylistModel::onImageLoaded);
}
int PlaylistModel::rowCount(const QModelIndex & /* parent */) const
{
return m_contents.count();
}
int PlaylistModel::columnCount(const QModelIndex & /* parent */) const
{
return 1;
}
QVariant PlaylistModel::data(const QModelIndex &index, int role) const
{
if (index.column() == 0)
{
if (!index.isValid())
return QVariant();
if (index.row() >= m_contents.size() || index.row() < 0)
return QVariant();
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::ToolTipRole:
return m_contents.at(index.row())["label_noext"];
case HASH:
return QVariant::fromValue(m_contents.at(index.row()));
case THUMBNAIL:
{
QPixmap *cachedPreview = m_cache.object(getCurrentTypeThumbnailPath(index));
if (cachedPreview)
return *cachedPreview;
return QVariant();
}
}
}
return QVariant();
}
Qt::ItemFlags PlaylistModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
}
bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole) {
QHash<QString, QString> hash = m_contents.at(index.row());
hash["label"] = value.toString();
hash["label_noext"] = QFileInfo(value.toString()).completeBaseName();
m_contents.replace(index.row(), hash);
emit dataChanged(index, index, { role });
return true;
}
return false;
}
QVariant PlaylistModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME);
else
return section + 1;
}
void PlaylistModel::setThumbnailType(const ThumbnailType type)
{
m_thumbnailType = type;
}
void PlaylistModel::setThumbnailCacheLimit(int limit)
{
m_cache.setMaxCost(limit * 1024);
}
QString PlaylistModel::getThumbnailPath(const QModelIndex &index, QString type) const
{
QByteArray extension;
QString extensionStr;
QString thumbnailFileNameNoExt;
int lastIndex = -1;
const QHash<QString, QString> &hash = m_contents.at(index.row());
lastIndex = hash["path"].lastIndexOf('.');
if (lastIndex >= 0)
{
extensionStr = hash["path"].mid(lastIndex + 1);
if (!extensionStr.isEmpty())
{
extension = extensionStr.toLower().toUtf8();
}
}
if (!extension.isEmpty() && m_imageFormats.contains(extension))
{
/* use thumbnail widgets to show regular image files */
return hash["path"];
}
else
{
thumbnailFileNameNoExt = hash["label_noext"];
thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_");
return QDir::cleanPath(QString(config_get_ptr()->paths.directory_thumbnails)) + "/" + hash.value("db_name") + "/" + type + "/" + thumbnailFileNameNoExt + ".png";
}
}
QString PlaylistModel::getCurrentTypeThumbnailPath(const QModelIndex &index) const
{
switch (m_thumbnailType)
{
case THUMBNAIL_TYPE_BOXART:
return getThumbnailPath(index, THUMBNAIL_BOXART);
case THUMBNAIL_TYPE_SCREENSHOT:
return getThumbnailPath(index, THUMBNAIL_SCREENSHOT);
case THUMBNAIL_TYPE_TITLE_SCREEN:
return getThumbnailPath(index, THUMBNAIL_TITLE);
default:
return QString();
}
}
void PlaylistModel::reloadThumbnail(const QModelIndex &index)
{
if (index.isValid()) {
reloadThumbnailPath(getCurrentTypeThumbnailPath(index));
loadThumbnail(index);
}
}
void PlaylistModel::reloadSystemThumbnails(const QString system)
{
int i = 0;
QString key;
QString path = QDir::cleanPath(QString(config_get_ptr()->paths.directory_thumbnails)) + "/" + system;
QList<QString> keys = m_cache.keys();
QList<QString> pending = m_pendingImages.values();
for (i = 0; i < keys.size(); i++)
{
key = keys.at(i);
if (key.startsWith(path))
m_cache.remove(key);
}
for (i = 0; i < pending.size(); i++)
{
key = pending.at(i);
if (key.startsWith(path))
m_pendingImages.remove(key);
}
}
void PlaylistModel::reloadThumbnailPath(const QString path)
{
m_cache.remove(path);
m_pendingImages.remove(path);
}
void PlaylistModel::loadThumbnail(const QModelIndex &index)
{
QString path = getCurrentTypeThumbnailPath(index);
if (!m_pendingImages.contains(path) && !m_cache.contains(path))
{
m_pendingImages.insert(path);
QtConcurrent::run(this, &PlaylistModel::loadImage, index, path);
}
}
void PlaylistModel::loadImage(const QModelIndex &index, const QString &path)
{
const QImage image = QImage(path);
if (!image.isNull())
emit imageLoaded(image, index, path);
}
void PlaylistModel::onImageLoaded(const QImage image, const QModelIndex &index, const QString &path)
{
QPixmap *pixmap = new QPixmap(QPixmap::fromImage(image));
const int cost = pixmap->width() * pixmap->height() * pixmap->depth() / (8 * 1024);
m_cache.insert(path, pixmap, cost);
if (index.isValid())
emit dataChanged(index, index, { THUMBNAIL });
m_pendingImages.remove(path);
}
inline static bool comp_hash_name_key_lower(const QHash<QString, QString> &lhs, const QHash<QString, QString> &rhs)
{
return lhs.value("name").toLower() < rhs.value("name").toLower();
@ -38,7 +246,6 @@ inline static bool comp_hash_label_key_lower(const QHash<QString, QString> &lhs,
return lhs.value("label").toLower() < rhs.value("label").toLower();
}
/* https://stackoverflow.com/questions/7246622/how-to-create-a-slider-with-a-non-linear-scale */
bool MainWindow::addDirectoryFilesToList(QProgressDialog *dialog, QStringList &list, QDir &dir, QStringList &extensions)
{
PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
@ -834,9 +1041,6 @@ void MainWindow::reloadPlaylists()
getPlaylistFiles();
/* block this signal because setData() would trigger an infinite loop */
disconnect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*)));
m_listWidget->clear();
m_listWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
m_listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
@ -968,13 +1172,11 @@ void MainWindow::reloadPlaylists()
}
}
connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*)));
}
QString MainWindow::getCurrentPlaylistPath()
{
QListWidgetItem *playlistItem = m_listWidget->currentItem();
QHash<QString, QString> contentHash;
QString playlistPath;
if (!playlistItem)
@ -1116,151 +1318,15 @@ void MainWindow::getPlaylistFiles()
m_playlistFiles = playlistDir.entryList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files, QDir::Name);
}
void MainWindow::addPlaylistItemsToGrid(const QStringList &paths, bool add)
{
QVector<QHash<QString, QString> > items;
int i;
if (paths.isEmpty())
return;
for (i = 0; i < paths.size(); i++)
{
int j;
QVector<QHash<QString, QString> > vec = getPlaylistItems(paths.at(i));
/* QVector::append() wasn't added until 5.5, so just do it the old fashioned way */
for (j = 0; j < vec.size(); j++)
{
if (add && m_allPlaylistsGridMaxCount > 0 && items.size() >= m_allPlaylistsGridMaxCount)
goto finish;
items.append(vec.at(j));
}
}
finish:
std::sort(items.begin(), items.end(), comp_hash_label_key_lower);
addPlaylistHashToGrid(items);
}
void MainWindow::addPlaylistHashToGrid(const QVector<QHash<QString, QString> > &items)
{
QScreen *screen = qApp->primaryScreen();
QSize screenSize = screen->size();
QListWidgetItem *currentItem = m_listWidget->currentItem();
settings_t *settings = config_get_ptr();
int i = 0;
int zoomValue = m_zoomSlider->value();
m_gridProgressBar->setMinimum(0);
m_gridProgressBar->setMaximum(qMax(0, items.count() - 1));
m_gridProgressBar->setValue(0);
for (i = 0; i < items.count(); i++)
{
const QHash<QString, QString> &hash = items.at(i);
QPointer<GridItem> item;
QPointer<ThumbnailLabel> label;
QString thumbnailFileNameNoExt;
QLabel *newLabel = NULL;
QSize thumbnailWidgetSizeHint(screenSize.width() / 8, screenSize.height() / 8);
QByteArray extension;
QString extensionStr;
QString imagePath;
int lastIndex = -1;
if (m_listWidget->currentItem() != currentItem)
{
/* user changed the current playlist before we finished loading... abort */
m_gridProgressWidget->hide();
break;
}
item = new GridItem();
lastIndex = hash["path"].lastIndexOf('.');
if (lastIndex >= 0)
{
extensionStr = hash["path"].mid(lastIndex + 1);
if (!extensionStr.isEmpty())
{
extension = extensionStr.toLower().toUtf8();
}
}
if (!extension.isEmpty() && m_imageFormats.contains(extension))
{
/* use thumbnail widgets to show regular image files */
imagePath = hash["path"];
}
else
{
thumbnailFileNameNoExt = hash["label_noext"];
thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_");
imagePath = QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png";
}
item->hash = hash;
item->widget = new ThumbnailWidget();
item->widget->setSizeHint(thumbnailWidgetSizeHint);
item->widget->setFixedSize(item->widget->sizeHint());
item->widget->setLayout(new QVBoxLayout());
item->widget->setObjectName("thumbnailWidget");
item->widget->setProperty("hash", QVariant::fromValue<QHash<QString, QString> >(hash));
item->widget->setProperty("image_path", imagePath);
connect(item->widget, SIGNAL(mouseDoubleClicked()), this, SLOT(onGridItemDoubleClicked()));
connect(item->widget, SIGNAL(mousePressed()), this, SLOT(onGridItemClicked()));
label = new ThumbnailLabel(item->widget);
label->setObjectName("thumbnailGridLabel");
item->label = label;
item->labelText = hash.value("label");
newLabel = new QLabel(item->labelText, item->widget);
newLabel->setObjectName("thumbnailQLabel");
newLabel->setAlignment(Qt::AlignCenter);
newLabel->setToolTip(item->labelText);
calcGridItemSize(item, zoomValue);
item->widget->layout()->addWidget(label);
item->widget->layout()->addWidget(newLabel);
qobject_cast<QVBoxLayout*>(item->widget->layout())->setStretchFactor(label, 1);
m_gridLayout->addWidgetDeferred(item->widget);
m_gridItems.append(item);
loadImageDeferred(item, imagePath);
if (i % 25 == 0)
{
/* Needed to update progress dialog while doing a lot of stuff on the main thread. */
qApp->processEvents();
}
m_gridProgressBar->setValue(i);
}
/* If there's only one entry, a min/max/value of all zero would make an indeterminate progress bar that never ends... so just hide it when we are done. */
if (m_gridProgressBar->value() == m_gridProgressBar->maximum())
m_gridProgressWidget->hide();
}
QVector<QHash<QString, QString> > MainWindow::getPlaylistItems(QString pathString)
void PlaylistModel::getPlaylistItems(QString path)
{
QByteArray pathArray;
QVector<QHash<QString, QString> > items;
const char *pathData = NULL;
playlist_t *playlist = NULL;
unsigned playlistSize = 0;
unsigned i = 0;
pathArray.append(pathString);
pathArray.append(path);
pathData = pathArray.constData();
playlist = playlist_init(pathData, COLLECTION_SIZE);
@ -1313,58 +1379,67 @@ QVector<QHash<QString, QString> > MainWindow::getPlaylistItems(QString pathStrin
hash["db_name"].remove(file_path_str(FILE_PATH_LPL_EXTENSION));
}
items.append(hash);
m_contents.append(hash);
}
playlist_free(playlist);
playlist = NULL;
return items;
}
void MainWindow::addPlaylistItemsToTable(const QStringList &paths, bool add)
void PlaylistModel::addPlaylistItems(const QStringList &paths, bool add)
{
QVector<QHash<QString, QString> > items;
int i;
if (paths.isEmpty())
return;
beginResetModel();
m_contents.clear();
for (i = 0; i < paths.size(); i++)
{
int j;
QVector<QHash<QString, QString> > vec = getPlaylistItems(paths.at(i));
/* QVector::append() wasn't added until 5.5, so just do it the old fashioned way */
for (j = 0; j < vec.size(); j++)
{
if (add && m_allPlaylistsListMaxCount > 0 && items.size() >= m_allPlaylistsListMaxCount)
goto finish;
items.append(vec.at(j));
}
getPlaylistItems(paths.at(i));
}
finish:
addPlaylistHashToTable(items);
endResetModel();
}
void MainWindow::addPlaylistHashToTable(const QVector<QHash<QString, QString> > &items)
void PlaylistModel::addDir(QString path, QFlags<QDir::Filter> showHidden)
{
QDir dir = path;
QStringList dirList;
int i = 0;
int oldRowCount = m_tableWidget->rowCount();
m_tableWidget->setRowCount(oldRowCount + items.count());
dirList = dir.entryList(QDir::NoDotAndDotDot |
QDir::Readable |
QDir::Files |
showHidden,
QDir::Name);
for (i = 0; i < items.count(); i++)
if (dirList.count() == 0)
return;
beginResetModel();
m_contents.clear();
for (i = 0; i < dirList.count(); i++)
{
QTableWidgetItem *labelItem = NULL;
const QHash<QString, QString> &hash = items.at(i);
QString fileName = dirList.at(i);
QHash<QString, QString> hash;
QString filePath(QDir::toNativeSeparators(dir.absoluteFilePath(fileName)));
QFileInfo fileInfo(filePath);
labelItem = new QTableWidgetItem(hash.value("label"));
labelItem->setData(Qt::UserRole, QVariant::fromValue<QHash<QString, QString> >(hash));
labelItem->setFlags(labelItem->flags() | Qt::ItemIsEditable);
hash["path"] = filePath;
hash["label"] = hash["path"];
hash["label_noext"] = fileInfo.completeBaseName();
hash["db_name"] = fileInfo.dir().dirName();
m_tableWidget->setItem(oldRowCount + i, 0, labelItem);
m_contents.append(hash);
}
endResetModel();
}
void MainWindow::setAllPlaylistsListMaxCount(int count)
@ -1382,4 +1457,3 @@ void MainWindow::setAllPlaylistsGridMaxCount(int count)
m_allPlaylistsGridMaxCount = count;
}

View File

@ -150,14 +150,14 @@ void MainWindow::onPlaylistThumbnailDownloadFinished()
emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": Code " + QString::number(code) + ": " + errorData);*/
}
m_playlistModel->reloadThumbnailPath(m_playlistThumbnailDownloadFile.fileName());
if (!m_playlistThumbnailDownloadWasCanceled && m_pendingPlaylistThumbnails.count() > 0)
{
QHash<QString, QString> nextThumbnail = m_pendingPlaylistThumbnails.takeAt(0);
ViewType viewType = getCurrentViewType();
if (viewType == VIEW_TYPE_ICONS)
emit gridItemChanged(reply->property("title").toString());
updateVisibleItems();
downloadNextPlaylistThumbnail(nextThumbnail.value("db_name"), nextThumbnail.value("label_noext"), nextThumbnail.value("type"));
}
else
@ -236,7 +236,7 @@ void MainWindow::downloadNextPlaylistThumbnail(QString system, QString title, QS
if (!m_playlistThumbnailDownloadFile.open(QIODevice::WriteOnly))
{
m_failedThumbnails++;
RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData);
if (m_pendingPlaylistThumbnails.count() > 0)
@ -290,9 +290,9 @@ void MainWindow::downloadPlaylistThumbnails(QString playlistPath)
QString system;
QString title;
QString type;
QVector<QHash<QString, QString> > playlistItems = getPlaylistItems(playlistPath);
settings_t *settings = config_get_ptr();
int i;
int count;
if (!settings || !playlistFile.exists())
return;
@ -302,12 +302,14 @@ void MainWindow::downloadPlaylistThumbnails(QString playlistPath)
m_failedThumbnails = 0;
m_playlistThumbnailDownloadWasCanceled = false;
if (playlistItems.count() == 0)
count = m_playlistModel->rowCount();
if (count == 0)
return;
for (i = 0; i < playlistItems.count(); i++)
for (i = 0; i < count; i++)
{
const QHash<QString, QString> &itemHash = playlistItems.at(i);
const QHash<QString, QString> &itemHash = m_playlistModel->index(i, 0).data(PlaylistModel::HASH).value< QHash<QString, QString> >();
QHash<QString, QString> hash;
QHash<QString, QString> hash2;
QHash<QString, QString> hash3;

View File

@ -154,6 +154,8 @@ void MainWindow::onThumbnailDownloadFinished()
{
RARCH_LOG("[Qt]: Thumbnail download finished successfully.\n");
/* reload thumbnail image */
m_playlistModel->reloadThumbnailPath(m_thumbnailDownloadFile.fileName());
updateVisibleItems();
emit itemChanged();
}
else

View File

@ -191,7 +191,6 @@ void MainWindow::onThumbnailPackDownloadFinished()
reply->disconnect();
reply->close();
reply->deleteLater();
}
void MainWindow::onThumbnailPackDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
@ -309,6 +308,11 @@ void MainWindow::onThumbnailPackExtractFinished(bool success)
emit showInfoMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_PACK_DOWNLOADED_SUCCESSFULLY));
QNetworkReply *reply = m_thumbnailPackDownloadReply.data();
m_playlistModel->reloadSystemThumbnails(reply->property("system").toString());
reply->deleteLater();
updateVisibleItems();
/* reload thumbnail image */
emit itemChanged();
}

View File

@ -31,7 +31,7 @@ extern "C" {
#include <string/stdstring.h>
#include <file/file_path.h>
#include <retro_miscellaneous.h>
};
}
#define CORE_NAME_COLUMN 0
#define CORE_VERSION_COLUMN 1

View File

@ -313,7 +313,7 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral(R"(
padding-left:5px;
padding-right:5px;
}
QTableWidget {
QTableView {
background-color:rgb(25,25,25);
alternate-background-color:rgb(40,40,40);
}
@ -422,14 +422,18 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral(R"(
QSizeGrip {
background-color:solid;
}
ThumbnailWidget#thumbnailWidget, ThumbnailLabel#thumbnailGridLabel, QLabel#thumbnailQLabel {
GridView::item {
background-color:rgb(40,40,40);
}
ThumbnailWidget#thumbnailWidgetSelected {
background-color:rgb(40,40,40);
GridView::item:selected {
border:3px solid %1;
}
QWidget#gridLayoutWidget {
GridView {
background-color:rgb(25,25,25);
selection-color: white;
qproperty-layout: "fixed";
}
GridItem {
qproperty-thumbnailvalign: "center";
}
)");

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@ extern "C" {
}
ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
QDialog(parent)
QDialog(mainwindow)
,m_mainwindow(mainwindow)
,m_settings(mainwindow->settings())
,m_saveGeometryCheckBox(new QCheckBox(this))
@ -27,6 +27,8 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
,m_saveLastTabCheckBox(new QCheckBox(this))
,m_showHiddenFilesCheckBox(new QCheckBox(this))
,m_themeComboBox(new QComboBox(this))
,m_thumbnailComboBox(new QComboBox(this))
,m_thumbnailCacheSpinBox(new QSpinBox(this))
,m_startupPlaylistComboBox(new QComboBox(this))
,m_highlightColorPushButton(new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CHOOSE), this))
,m_highlightColor()
@ -45,6 +47,13 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK), MainWindow::THEME_DARK);
m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM), MainWindow::THEME_CUSTOM);
m_thumbnailComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART), THUMBNAIL_TYPE_BOXART);
m_thumbnailComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT), THUMBNAIL_TYPE_SCREENSHOT);
m_thumbnailComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN), THUMBNAIL_TYPE_TITLE_SCREEN);
m_thumbnailCacheSpinBox->setSuffix(" MB");
m_thumbnailCacheSpinBox->setRange(0, 99999);
m_allPlaylistsListMaxCountSpinBox->setRange(0, 99999);
m_allPlaylistsGridMaxCountSpinBox->setRange(0, 99999);
@ -67,6 +76,8 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT), m_allPlaylistsListMaxCountSpinBox);
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT), m_allPlaylistsGridMaxCountSpinBox);
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST), m_startupPlaylistComboBox);
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE), m_thumbnailComboBox);
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT), m_thumbnailCacheSpinBox);
form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME), m_themeComboBox);
form->addRow(m_highlightColorLabel, m_highlightColorPushButton);
@ -77,9 +88,16 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) :
loadViewOptions();
connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int)));
connect(m_thumbnailComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThumbnailComboBoxIndexChanged(int)));
connect(m_highlightColorPushButton, SIGNAL(clicked()), this, SLOT(onHighlightColorChoose()));
}
void ViewOptionsDialog::onThumbnailComboBoxIndexChanged(int index)
{
ThumbnailType type = static_cast<ThumbnailType>(m_thumbnailComboBox->currentData().value<int>());
m_mainwindow->setCurrentThumbnailType(type);
}
void ViewOptionsDialog::onThemeComboBoxIndexChanged(int)
{
MainWindow::Theme theme = static_cast<MainWindow::Theme>(m_themeComboBox->currentData(Qt::UserRole).toInt());
@ -138,6 +156,7 @@ void ViewOptionsDialog::loadViewOptions()
QVector<QPair<QString, QString> > playlists = m_mainwindow->getPlaylists();
QString initialPlaylist = m_settings->value("initial_playlist", m_mainwindow->getSpecialPlaylistPath(SPECIAL_PLAYLIST_HISTORY)).toString();
int themeIndex = 0;
int thumbnailIndex = 0;
int playlistIndex = 0;
int i;
@ -148,12 +167,18 @@ void ViewOptionsDialog::loadViewOptions()
m_suggestLoadedCoreFirstCheckBox->setChecked(m_settings->value("suggest_loaded_core_first", false).toBool());
m_allPlaylistsListMaxCountSpinBox->setValue(m_settings->value("all_playlists_list_max_count", 0).toInt());
m_allPlaylistsGridMaxCountSpinBox->setValue(m_settings->value("all_playlists_grid_max_count", 5000).toInt());
m_thumbnailCacheSpinBox->setValue(m_settings->value("thumbnail_cache_limit", 512).toInt());
themeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString()));
if (m_themeComboBox->count() > themeIndex)
m_themeComboBox->setCurrentIndex(themeIndex);
thumbnailIndex = m_thumbnailComboBox->findData(m_mainwindow->getThumbnailTypeFromString(m_settings->value("icon_view_thumbnail_type", "boxart").toString()));
if (m_thumbnailComboBox->count() > thumbnailIndex)
m_thumbnailComboBox->setCurrentIndex(thumbnailIndex);
if (highlightColor.isValid())
{
m_highlightColor = highlightColor;
@ -204,12 +229,15 @@ void ViewOptionsDialog::saveViewOptions()
m_settings->setValue("all_playlists_list_max_count", m_allPlaylistsListMaxCountSpinBox->value());
m_settings->setValue("all_playlists_grid_max_count", m_allPlaylistsGridMaxCountSpinBox->value());
m_settings->setValue("initial_playlist", m_startupPlaylistComboBox->currentData(Qt::UserRole).toString());
m_settings->setValue("icon_view_thumbnail_type", m_mainwindow->getCurrentThumbnailTypeString());
m_settings->setValue("thumbnail_cache_limit", m_thumbnailCacheSpinBox->value());
if (!m_mainwindow->customThemeString().isEmpty())
m_settings->setValue("custom_theme", m_customThemePath);
m_mainwindow->setAllPlaylistsListMaxCount(m_allPlaylistsListMaxCountSpinBox->value());
m_mainwindow->setAllPlaylistsGridMaxCount(m_allPlaylistsGridMaxCountSpinBox->value());
m_mainwindow->setThumbnailCacheLimit(m_thumbnailCacheSpinBox->value());
}
void ViewOptionsDialog::onAccepted()
@ -229,11 +257,12 @@ void ViewOptionsDialog::onRejected()
void ViewOptionsDialog::showDialog()
{
loadViewOptions();
setWindowFlags(windowFlags() | Qt::Tool);
show();
activateWindow();
}
void ViewOptionsDialog::hideDialog()
{
reject();
}

View File

@ -24,6 +24,7 @@ public slots:
void onRejected();
private slots:
void onThemeComboBoxIndexChanged(int index);
void onThumbnailComboBoxIndexChanged(int index);
void onHighlightColorChoose();
private:
void loadViewOptions();
@ -37,6 +38,8 @@ private:
QCheckBox *m_saveLastTabCheckBox;
QCheckBox *m_showHiddenFilesCheckBox;
QComboBox *m_themeComboBox;
QComboBox *m_thumbnailComboBox;
QSpinBox *m_thumbnailCacheSpinBox;
QComboBox *m_startupPlaylistComboBox;
QPushButton *m_highlightColorPushButton;
QColor m_highlightColor;

View File

@ -293,11 +293,12 @@ static void* ui_companion_qt_init(void)
widget->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(widget, SIGNAL(filesDropped(QStringList)), mainwindow, SLOT(onPlaylistFilesDropped(QStringList)));
QObject::connect(widget, SIGNAL(enterPressed()), mainwindow, SLOT(onDropWidgetEnterPressed()));
QObject::connect(widget, SIGNAL(deletePressed()), mainwindow, SLOT(deleteCurrentPlaylistItem()));
QObject::connect(widget, SIGNAL(customContextMenuRequested(const QPoint&)), mainwindow, SLOT(onFileDropWidgetContextMenuRequested(const QPoint&)));
layout = new QVBoxLayout();
layout->addWidget(mainwindow->contentTableWidget());
layout->addWidget(mainwindow->contentTableView());
layout->addWidget(mainwindow->contentGridWidget());
widget->setLayout(layout);
@ -513,6 +514,11 @@ static void* ui_companion_qt_init(void)
if (qsettings->contains("all_playlists_grid_max_count"))
mainwindow->setAllPlaylistsGridMaxCount(qsettings->value("all_playlists_grid_max_count", 5000).toInt());
if (qsettings->contains("thumbnail_cache_limit"))
mainwindow->setThumbnailCacheLimit(qsettings->value("thumbnail_cache_limit", 500).toInt());
else
mainwindow->setThumbnailCacheLimit(500);
if (qsettings->contains("geometry"))
if (qsettings->contains("save_geometry"))
mainwindow->restoreGeometry(qsettings->value("geometry").toByteArray());
@ -555,6 +561,25 @@ static void* ui_companion_qt_init(void)
else
mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST);
if (qsettings->contains("icon_view_thumbnail_type"))
{
QString thumbnailType = qsettings->value("icon_view_thumbnail_type", "boxart").toString();
if (thumbnailType == "boxart")
mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART);
else if (thumbnailType == "screenshot")
mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_SCREENSHOT);
else if (thumbnailType == "title")
mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_TITLE_SCREEN);
else
mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART);
/* we set it to the same thing a second time so that m_lastThumbnailType is also equal to the startup view type */
mainwindow->setCurrentThumbnailType(mainwindow->getCurrentThumbnailType());
}
else
mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST);
/* We make sure to hook up the tab widget callback only after the tabs themselves have been added,
* but before changing to a specific one, to avoid the callback firing before the view type is set.
*/

View File

@ -22,7 +22,7 @@
#include <QMainWindow>
#include <QTreeView>
#include <QListWidget>
#include <QTableWidget>
#include <QTableView>
#include <QFrame>
#include <QWidget>
#include <QDialog>
@ -38,6 +38,10 @@
#include <QElapsedTimer>
#include <QSslError>
#include <QNetworkReply>
#include <QStyledItemDelegate>
#include <QCache>
#include <QSortFilterProxyModel>
#include <QDir>
extern "C" {
#include <retro_assert.h>
@ -84,7 +88,7 @@ class LoadCoreWindow;
class MainWindow;
class ThumbnailWidget;
class ThumbnailLabel;
class FlowLayout;
class GridView;
class ShaderParamsDialog;
class CoreOptionsDialog;
class CoreInfoDialog;
@ -96,19 +100,58 @@ enum SpecialPlaylist
SPECIAL_PLAYLIST_HISTORY
};
class GridItem : public QObject
enum ThumbnailType
{
THUMBNAIL_TYPE_BOXART,
THUMBNAIL_TYPE_SCREENSHOT,
THUMBNAIL_TYPE_TITLE_SCREEN,
};
class PlaylistModel : public QAbstractListModel
{
Q_OBJECT
public:
GridItem();
QPointer<ThumbnailWidget> widget;
QPointer<ThumbnailLabel> label;
QHash<QString, QString> hash;
QImage image;
QPixmap pixmap;
QFutureWatcher<GridItem*> imageWatcher;
QString labelText;
public:
enum Roles
{
HASH = Qt::UserRole + 1,
THUMBNAIL
};
PlaylistModel(QObject *parent = 0);
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
void addPlaylistItems(const QStringList &paths, bool add = false);
void addDir(QString path, QFlags<QDir::Filter> showHidden);
void setThumbnailType(const ThumbnailType type);
void loadThumbnail(const QModelIndex &index);
void reloadThumbnail(const QModelIndex &index);
void reloadThumbnailPath(const QString path);
void reloadSystemThumbnails(const QString system);
void setThumbnailCacheLimit(int limit);
signals:
void imageLoaded(const QImage image, const QModelIndex &index, const QString &path);
private slots:
void onImageLoaded(const QImage image, const QModelIndex &index, const QString &path);
private:
QVector<QHash<QString, QString> > m_contents;
QCache<QString, QPixmap> m_cache;
QSet<QString> m_pendingImages;
QVector<QByteArray> m_imageFormats;
QRegularExpression m_fileSanitizerRegex;
ThumbnailType m_thumbnailType = THUMBNAIL_TYPE_BOXART;
QString getThumbnailPath(const QModelIndex &index, QString type) const;
QString getCurrentTypeThumbnailPath(const QModelIndex &index) const;
void getPlaylistItems(QString path);
void loadImage(const QModelIndex &index, const QString &path);
};
class ThumbnailWidget : public QFrame
@ -164,17 +207,12 @@ protected slots:
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
};
class TableWidget : public QTableWidget
class TableView : public QTableView
{
Q_OBJECT
public:
TableWidget(QWidget *parent = 0);
TableView(QWidget *parent = 0);
bool isEditorOpen();
signals:
void enterPressed();
void deletePressed();
protected:
void keyPressEvent(QKeyEvent *event);
};
class ListWidget : public QListWidget
@ -233,6 +271,26 @@ public slots:
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
{
Q_OBJECT
@ -263,9 +321,10 @@ public:
MainWindow(QWidget *parent = NULL);
~MainWindow();
TreeView* dirTreeView();
PlaylistModel* playlistModel();
ListWidget* playlistListWidget();
TableWidget* contentTableWidget();
FlowLayout* contentGridLayout();
TableView* contentTableView();
GridView* contentGridView();
QWidget* contentGridWidget();
QWidget* searchWidget();
QLineEdit* searchLineEdit();
@ -289,20 +348,26 @@ public:
bool setCustomThemeFile(QString filePath);
void setCustomThemeString(QString qss);
const QString& customThemeString() const;
GridItem* doDeferredImageLoad(GridItem *item, QString path);
void setCurrentViewType(ViewType viewType);
QString getCurrentViewTypeString();
ViewType getCurrentViewType();
void setCurrentThumbnailType(ThumbnailType thumbnailType);
QString getCurrentThumbnailTypeString();
ThumbnailType getCurrentThumbnailType();
ThumbnailType getThumbnailTypeFromString(QString thumbnailType);
void setAllPlaylistsListMaxCount(int count);
void setAllPlaylistsGridMaxCount(int count);
void setThumbnailCacheLimit(int count);
PlaylistEntryDialog* playlistEntryDialog();
void addFilesToPlaylist(QStringList files);
QString getCurrentPlaylistPath();
QModelIndex getCurrentContentIndex();
QHash<QString, QString> getCurrentContentHash();
static double lerp(double x, double y, double a, double b, double d);
QString getSpecialPlaylistPath(SpecialPlaylist playlist);
QVector<QPair<QString, QString> > getPlaylists();
QString getScrubbedString(QString str);
void setDefaultCustomProperties();
signals:
void thumbnailChanged(const QPixmap &pixmap);
@ -317,6 +382,7 @@ signals:
void showInfoMessageDeferred(QString msg);
void extractArchiveDeferred(QString path, QString extractionDir, QString tempExtension, retro_task_callback_t cb);
void itemChanged();
void updateThumbnails();
void gridItemChanged(QString title);
void gotThumbnailDownload(QString system, QString title);
void scrollToDownloads(QString path);
@ -327,15 +393,13 @@ public slots:
void onBrowserUpClicked();
void onBrowserStartClicked();
void initContentTableWidget();
void initContentGridLayout();
void onViewClosedDocksAboutToShow();
void onShowHiddenDockWidgetAction();
void setCoreActions();
void onRunClicked();
void loadContent(const QHash<QString, QString> &contentHash);
void onStartCoreClicked();
void onTableWidgetEnterPressed();
void onTableWidgetDeletePressed();
void onDropWidgetEnterPressed();
void selectBrowserDir(QString path);
void resizeThumbnails(bool one, bool two, bool three);
void onResizeThumbnailOne();
@ -365,24 +429,20 @@ public slots:
void downloadAllThumbnails(QString system, QUrl url = QUrl());
void downloadPlaylistThumbnails(QString playlistPath);
void downloadNextPlaylistThumbnail(QString system, QString title, QString type, QUrl url = QUrl());
void changeThumbnailType(ThumbnailType type);
private slots:
void onLoadCoreClicked(const QStringList &extensionFilters = QStringList());
void onUnloadCoreMenuAction();
void onTimeout();
void onCoreLoaded();
void onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
void onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
void onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous);
void onCurrentTableItemDataChanged(QTableWidgetItem *item);
void onCurrentListItemDataChanged(QListWidgetItem *item);
void currentItemChanged(const QHash<QString, QString> &hash);
void currentItemChanged(const QModelIndex &index);
void onSearchEnterPressed();
void onSearchLineEditEdited(const QString &text);
void addPlaylistItemsToTable(const QStringList &paths, bool all = false);
void addPlaylistHashToTable(const QVector<QHash<QString, QString> > &items);
void addPlaylistItemsToGrid(const QStringList &paths, bool all = false);
void addPlaylistHashToGrid(const QVector<QHash<QString, QString> > &items);
void onContentItemDoubleClicked(QTableWidgetItem *item);
void onContentItemDoubleClicked(const QModelIndex &index);
void onCoreLoadWindowClosed();
void onTreeViewItemsSelected(QModelIndexList selectedIndexes);
void onSearchResetClicked();
@ -390,13 +450,7 @@ private slots:
void onFileBrowserTreeContextMenuRequested(const QPoint &pos);
void onPlaylistWidgetContextMenuRequested(const QPoint &pos);
void onStopClicked();
void onDeferredImageLoaded();
void onZoomValueChanged(int value);
void onContentGridInited();
void onUpdateGridItemPixmapFromImage(GridItem *item);
void onPendingItemUpdates();
void onGridItemDoubleClicked();
void onGridItemClicked(ThumbnailWidget *thumbnailWidget = NULL);
void onPlaylistFilesDropped(QStringList files);
void onShaderParamsClicked();
void onCoreOptionsClicked();
@ -404,7 +458,6 @@ private slots:
void onShowInfoMessage(QString msg);
void onContributorsClicked();
void onItemChanged();
void onGridItemChanged(QString title);
void onFileSystemDirLoaded(const QString &path);
void onDownloadScroll(QString path);
void onDownloadScrollAgain(QString path);
@ -439,14 +492,14 @@ private slots:
void onPlaylistThumbnailDownloadReadyRead();
void onPlaylistThumbnailDownloadCanceled();
void startTimer();
void updateVisibleItems();
private:
void setCurrentCoreLabel();
void getPlaylistFiles();
bool isCoreLoaded();
bool isContentLessCore();
void removeGridItems();
void loadImageDeferred(GridItem *item, QString path);
void calcGridItemSize(GridItem *item, int zoomValue);
bool updateCurrentPlaylistEntry(const QHash<QString, QString> &contentHash);
int extractArchive(QString path);
void removeUpdateTempFiles();
@ -454,8 +507,9 @@ private:
void renamePlaylistItem(QListWidgetItem *item, QString newName);
bool currentPlaylistIsSpecial();
bool currentPlaylistIsAll();
QVector<QHash<QString, QString> > getPlaylistItems(QString pathString);
PlaylistModel *m_playlistModel;
QSortFilterProxyModel *m_proxyModel;
LoadCoreWindow *m_loadCoreWindow;
QTimer *m_timer;
QString m_currentCore;
@ -464,7 +518,7 @@ private:
TreeView *m_dirTree;
QFileSystemModel *m_dirModel;
ListWidget *m_listWidget;
TableWidget *m_tableWidget;
TableView *m_tableView;
QWidget *m_searchWidget;
QLineEdit *m_searchLineEdit;
QDockWidget *m_searchDock;
@ -496,19 +550,19 @@ private:
QListWidgetItem *m_historyPlaylistsItem;
QIcon m_folderIcon;
QString m_customThemeString;
FlowLayout *m_gridLayout;
GridView *m_gridView;
QWidget *m_gridWidget;
QScrollArea *m_gridScrollArea;
QVector<QPointer<GridItem> > m_gridItems;
QWidget *m_gridLayoutWidget;
QSlider *m_zoomSlider;
int m_lastZoomSliderValue;
QList<GridItem*> m_pendingItemUpdates;
ViewType m_viewType;
ThumbnailType m_thumbnailType;
QProgressBar *m_gridProgressBar;
QWidget *m_gridProgressWidget;
QHash<QString, QString> m_currentGridHash;
ViewType m_lastViewType;
ThumbnailType m_lastThumbnailType;
QPointer<ThumbnailWidget> m_currentGridWidget;
int m_allPlaylistsListMaxCount;
int m_allPlaylistsGridMaxCount;
@ -540,6 +594,9 @@ private:
bool m_playlistThumbnailDownloadWasCanceled;
QString m_pendingDirScrollPath;
QTimer *m_thumbnailTimer;
GridItem m_gridItem;
protected:
void closeEvent(QCloseEvent *event);
void keyPressEvent(QKeyEvent *event);