From 5f7abb068bd6caafe913255900627253cdf14802 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Wed, 2 May 2018 23:04:10 -0400 Subject: [PATCH 01/23] Qt: initial grid layout work --- Makefile.common | 3 +- ui/drivers/qt/flowlayout.cpp | 200 +++++++++++++++++++++++++++++++++ ui/drivers/qt/flowlayout.h | 89 +++++++++++++++ ui/drivers/qt/ui_qt_window.cpp | 164 +++++++++++++++++++++++++-- ui/drivers/ui_qt.cpp | 3 + ui/drivers/ui_qt.h | 18 +++ 6 files changed, 466 insertions(+), 11 deletions(-) create mode 100644 ui/drivers/qt/flowlayout.cpp create mode 100644 ui/drivers/qt/flowlayout.h diff --git a/Makefile.common b/Makefile.common index dce6b77e31..c522edea25 100644 --- a/Makefile.common +++ b/Makefile.common @@ -332,7 +332,8 @@ OBJ += ui/drivers/ui_qt.o \ ui/drivers/qt/ui_qt_window.o \ 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/ui_qt_msg_window.o \ + ui/drivers/qt/flowlayout.o MOC_HEADERS += ui/drivers/ui_qt.h \ ui/drivers/qt/ui_qt_load_core_window.h diff --git a/ui/drivers/qt/flowlayout.cpp b/ui/drivers/qt/flowlayout.cpp new file mode 100644 index 0000000000..0dd3a1f606 --- /dev/null +++ b/ui/drivers/qt/flowlayout.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +#include + +#include "flowlayout.h" +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + 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 0; +} + +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; + QLayoutItem *item; + foreach (item, itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} + +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + QLayoutItem *item; + foreach (item, itemList) { + QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + int 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 +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else { + return static_cast(parent)->spacing(); + } +} diff --git a/ui/drivers/qt/flowlayout.h b/ui/drivers/qt/flowlayout.h new file mode 100644 index 0000000000..436ba5e4c6 --- /dev/null +++ b/ui/drivers/qt/flowlayout.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +/* bparker: Removed C++11 override keyword from original source */ + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include + +class FlowLayout : public QLayout +{ +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); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; + +#endif // FLOWLAYOUT_H diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 12e59b2065..1261950f71 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -33,6 +33,7 @@ #include "../ui_qt.h" #include "ui_qt_load_core_window.h" #include "ui_qt_themes.h" +#include "flowlayout.h" extern "C" { #include "../../../version.h" @@ -487,13 +488,30 @@ MainWindow::MainWindow(QWidget *parent) : ,m_historyPlaylistsItem(NULL) ,m_folderIcon() ,m_customThemeString() + ,m_gridLayout(new FlowLayout()) + ,m_gridWidget(new QWidget(this)) + ,m_gridScrollArea(new QScrollArea(m_gridWidget)) + ,m_gridItems() { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath(); QToolButton *searchResetButton = NULL; + QWidget *gridLayoutWidget = new QWidget(); int i = 0; + m_gridWidget->setLayout(new QVBoxLayout()); + + gridLayoutWidget->setLayout(m_gridLayout); + + m_gridScrollArea->setAlignment(Qt::AlignCenter); + m_gridScrollArea->setFrameShape(QFrame::NoFrame); + m_gridScrollArea->setWidgetResizable(true); + m_gridScrollArea->setWidget(gridLayoutWidget); + + m_gridWidget->layout()->addWidget(m_gridScrollArea); + m_gridWidget->layout()->setAlignment(Qt::AlignCenter); + m_tableWidget->setAlternatingRowColors(true); m_logWidget->setObjectName("logWidget"); @@ -658,6 +676,7 @@ MainWindow::~MainWindow() delete m_thumbnailPixmap2; if (m_thumbnailPixmap3) delete m_thumbnailPixmap3; + removeGridItems(); } void MainWindow::showWelcomeScreen() @@ -2512,7 +2531,8 @@ void MainWindow::onCurrentListItemChanged(QListWidgetItem *current, QListWidgetI if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) != msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) return; - initContentTableWidget(); + //initContentTableWidget(); + initContentGridLayout(); setCoreActions(); } @@ -2521,6 +2541,16 @@ TableWidget* MainWindow::contentTableWidget() return m_tableWidget; } +QWidget* MainWindow::contentGridWidget() +{ + return m_gridWidget; +} + +FlowLayout* MainWindow::contentGridLayout() +{ + return m_gridLayout; +} + void MainWindow::onBrowserDownloadsClicked() { settings_t *settings = config_get_ptr(); @@ -2700,6 +2730,98 @@ void MainWindow::onLoadCoreClicked(const QStringList &extensionFilters) m_loadCoreWindow->initCoreList(extensionFilters); } +void MainWindow::removeGridItems() +{ + if (m_gridItems.count() > 0) + { + QMutableListIterator items(m_gridItems); + + while (items.hasNext()) + { + GridItem *item = items.next(); + + if (item) + { + items.remove(); + + m_gridLayout->removeWidget(item->widget); + + delete item->widget; + delete item; + } + } + } +} + +void MainWindow::addPlaylistItemsToGrid(QString pathString) +{ + QList > items = getPlaylistItems(pathString); + settings_t *settings = config_get_ptr(); + int i = 0; + + for (i = 0; i < items.count(); i++) + { + const QHash &hash = items.at(i); + GridItem *item = new GridItem(); + ThumbnailLabel *label = NULL; + QPixmap pixmap; + QString thumbnailFileNameNoExt; + QLabel *newLabel = NULL; + + thumbnailFileNameNoExt = hash["label_noext"]; + thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); + + item->hash = hash; + item->widget = new ThumbnailWidget(); + item->widget->setFixedSize(item->widget->sizeHint()); + item->widget->setLayout(new QVBoxLayout()); + item->widget->setStyleSheet("background-color: #555555"); + + label = new ThumbnailLabel(item->widget); + + pixmap = QPixmap(QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"); + + label->setPixmap(pixmap); + + item->widget->layout()->addWidget(label); + + newLabel = new QLabel(hash.value("label"), item->widget); + newLabel->setAlignment(Qt::AlignCenter); + + item->widget->layout()->addWidget(newLabel); + qobject_cast(item->widget->layout())->setStretchFactor(label, 1); + + m_gridLayout->addWidget(item->widget); + m_gridItems.append(item); + } +} + +void MainWindow::initContentGridLayout() +{ + QListWidgetItem *item = m_listWidget->currentItem(); + QString path; + + if (!item) + return; + + removeGridItems(); + + path = item->data(Qt::UserRole).toString(); + + if (path == ALL_PLAYLISTS_TOKEN) + { + settings_t *settings = config_get_ptr(); + QDir playlistDir(settings->paths.directory_playlist); + + foreach (QString playlist, m_playlistFiles) + { + addPlaylistItemsToGrid(playlistDir.absoluteFilePath(playlist)); + } + } + else + addPlaylistItemsToGrid(path); +} + void MainWindow::initContentTableWidget() { QListWidgetItem *item = m_listWidget->currentItem(); @@ -2759,14 +2881,14 @@ void MainWindow::initContentTableWidget() onSearchEnterPressed(); } -void MainWindow::addPlaylistItemsToTable(QString pathString) +QList > MainWindow::getPlaylistItems(QString pathString) { QByteArray pathArray; + QList > items; const char *pathData = NULL; playlist_t *playlist = NULL; unsigned playlistSize = 0; unsigned i = 0; - int oldRowCount = m_tableWidget->rowCount(); pathArray.append(pathString); pathData = pathArray.constData(); @@ -2774,8 +2896,6 @@ void MainWindow::addPlaylistItemsToTable(QString pathString) playlist = playlist_init(pathData, COLLECTION_SIZE); playlistSize = playlist_get_size(playlist); - m_tableWidget->setRowCount(oldRowCount + playlistSize); - for (i = 0; i < playlistSize; i++) { const char *path = NULL; @@ -2784,7 +2904,6 @@ void MainWindow::addPlaylistItemsToTable(QString pathString) const char *core_name = NULL; const char *crc32 = NULL; const char *db_name = NULL; - QTableWidgetItem *labelItem = NULL; QHash hash; playlist_get_index(playlist, i, @@ -2822,15 +2941,40 @@ void MainWindow::addPlaylistItemsToTable(QString pathString) hash["db_name"].remove(file_path_str(FILE_PATH_LPL_EXTENSION)); } - labelItem = new QTableWidgetItem(hash["label"]); + items.append(hash); + } + + playlist_free(playlist); + playlist = NULL; + + return items; +} + +void MainWindow::addPlaylistItemsToTable(QString pathString) +{ + QList > items = getPlaylistItems(pathString); + int i = 0; + int oldRowCount = m_tableWidget->rowCount(); + + m_tableWidget->setRowCount(oldRowCount + items.count()); + + for (i = 0; i < items.count(); i++) + { + const char *path = NULL; + const char *label = NULL; + const char *core_path = NULL; + const char *core_name = NULL; + const char *crc32 = NULL; + const char *db_name = NULL; + QTableWidgetItem *labelItem = NULL; + const QHash &hash = items.at(i); + + labelItem = new QTableWidgetItem(hash.value("label")); labelItem->setData(Qt::UserRole, QVariant::fromValue >(hash)); labelItem->setFlags(labelItem->flags() & ~Qt::ItemIsEditable); m_tableWidget->setItem(oldRowCount + i, 0, labelItem); } - - playlist_free(playlist); - playlist = NULL; } void MainWindow::keyPressEvent(QKeyEvent *event) diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index 6a5272b39b..e800e4920b 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -256,6 +256,9 @@ static void* ui_companion_qt_init(void) layout = new QVBoxLayout(); layout->addWidget(mainwindow->contentTableWidget()); + layout->addWidget(mainwindow->contentGridWidget()); + + mainwindow->contentTableWidget()->hide(); widget->setLayout(layout); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 5ebd251212..725196ecb0 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -57,6 +57,14 @@ class QStyle; class QScrollArea; class LoadCoreWindow; class MainWindow; +class ThumbnailWidget; +class FlowLayout; + +struct GridItem +{ + ThumbnailWidget *widget; + QHash hash; +}; class ThumbnailWidget : public QWidget { @@ -223,6 +231,8 @@ public: TreeView* dirTreeView(); QListWidget* playlistListWidget(); TableWidget* contentTableWidget(); + FlowLayout* contentGridLayout(); + QWidget* contentGridWidget(); QWidget* searchWidget(); QLineEdit* searchLineEdit(); QComboBox* launchWithComboBox(); @@ -259,6 +269,7 @@ public slots: void onBrowserUpClicked(); void onBrowserStartClicked(); void initContentTableWidget(); + void initContentGridLayout(); void onViewClosedDocksAboutToShow(); void onShowHiddenDockWidgetAction(); void setCoreActions(); @@ -288,6 +299,7 @@ private slots: void onSearchEnterPressed(); void onSearchLineEditEdited(const QString &text); void addPlaylistItemsToTable(QString path); + void addPlaylistItemsToGrid(QString path); void onContentItemDoubleClicked(QTableWidgetItem *item); void onCoreLoadWindowClosed(); void onTabWidgetIndexChanged(int index); @@ -303,6 +315,8 @@ private: void getPlaylistFiles(); bool isCoreLoaded(); bool isContentLessCore(); + void removeGridItems(); + QList > getPlaylistItems(QString pathString); LoadCoreWindow *m_loadCoreWindow; QTimer *m_timer; @@ -344,6 +358,10 @@ private: QListWidgetItem *m_historyPlaylistsItem; QIcon m_folderIcon; QString m_customThemeString; + FlowLayout *m_gridLayout; + QWidget *m_gridWidget; + QScrollArea *m_gridScrollArea; + QList m_gridItems; protected: void closeEvent(QCloseEvent *event); From f79ba4885a33a30cd7ed4377ba01e5a4c1f610c6 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 00:49:43 -0400 Subject: [PATCH 02/23] Qt: threaded grid image loading --- Makefile.common | 4 ++-- qb/config.libs.sh | 4 +++- ui/drivers/qt/ui_qt_window.cpp | 41 ++++++++++++++++++++++++++++++---- ui/drivers/ui_qt.h | 12 ++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/Makefile.common b/Makefile.common index c522edea25..44d9185717 100644 --- a/Makefile.common +++ b/Makefile.common @@ -338,9 +338,9 @@ OBJ += ui/drivers/ui_qt.o \ MOC_HEADERS += ui/drivers/ui_qt.h \ ui/drivers/qt/ui_qt_load_core_window.h -DEFINES += $(QT5CORE_CFLAGS) $(QT5GUI_CFLAGS) $(QT5WIDGETS_CFLAGS) -DHAVE_MAIN +DEFINES += $(QT5CORE_CFLAGS) $(QT5GUI_CFLAGS) $(QT5WIDGETS_CFLAGS) $(QT5CONCURRENT_CFLAGS) -DHAVE_MAIN #DEFINES += $(QT5WEBENGINE_CFLAGS) -LIBS += $(QT5CORE_LIBS) $(QT5GUI_LIBS) $(QT5WIDGETS_LIBS) +LIBS += $(QT5CORE_LIBS) $(QT5GUI_LIBS) $(QT5WIDGETS_LIBS) $(QT5CONCURRENT_LIBS) #LIBS += $(QT5WEBENGINE_LIBS) NEED_CXX_LINKER = 1 diff --git a/qb/config.libs.sh b/qb/config.libs.sh index e36974ceef..509dc480fb 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -276,14 +276,16 @@ if [ "$HAVE_QT" != 'no' ] && [ "$MOC_PATH" != 'none' ]; then check_pkgconf QT5CORE Qt5Core 5.2 check_pkgconf QT5GUI Qt5Gui 5.2 check_pkgconf QT5WIDGETS Qt5Widgets 5.2 + check_pkgconf QT5CONCURRENT Qt5Concurrent 5.2 #check_pkgconf QT5WEBENGINE Qt5WebEngine 5.4 check_val '' QT5CORE -lQt5Core QT5CORE check_val '' QT5GUI -lQt5Gui QT5GUI check_val '' QT5WIDGETS -lQt5Widgets QT5WIDGETS + check_val '' QT5CONCURRENT -lQt5Widgets QT5CONCURRENT #check_val '' QT5WEBENGINE -lQt5WebEngine QT5WEBENGINE - if [ "$HAVE_QT5CORE" = "no" ] || [ "$HAVE_QT5GUI" = "no" ] || [ "$HAVE_QT5WIDGETS" = "no" ]; then + if [ "$HAVE_QT5CORE" = "no" ] || [ "$HAVE_QT5GUI" = "no" ] || [ "$HAVE_QT5WIDGETS" = "no" ] || [ "$HAVE_QT5CONCURRENT" = "no" ]; then die : 'Notice: Not building Qt support, required libraries were not found.' HAVE_QT=no else diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 1261950f71..be9edf4ca2 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "../ui_qt.h" #include "ui_qt_load_core_window.h" @@ -101,6 +102,16 @@ static void scan_finished_handler(void *task_data, void *user_data, const char * } #endif +GridItem::GridItem() : + widget(NULL) + ,label(NULL) + ,hash() + ,image() + ,pixmap() + ,imageWatcher() +{ +} + TreeView::TreeView(QWidget *parent) : QTreeView(parent) { @@ -2753,6 +2764,29 @@ void MainWindow::removeGridItems() } } +void MainWindow::onDeferredImageLoaded() +{ + const QFutureWatcher *watcher = static_cast*>(sender()); + GridItem *item = watcher->result(); + + if (!item->image.isNull()) + item->label->setPixmap(QPixmap::fromImage(item->image)); +} + +void MainWindow::loadImageDeferred(GridItem *item, QString path) +{ + connect(&item->imageWatcher, SIGNAL(finished()), this, SLOT(onDeferredImageLoaded()), Qt::QueuedConnection); + item->imageWatcher.setFuture(QtConcurrent::run(this, &MainWindow::doDeferredImageLoad, item, path)); +} + +GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) +{ + /* this runs in another thread */ + item->image = QImage(path); + + return item; +} + void MainWindow::addPlaylistItemsToGrid(QString pathString) { QList > items = getPlaylistItems(pathString); @@ -2764,7 +2798,6 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) const QHash &hash = items.at(i); GridItem *item = new GridItem(); ThumbnailLabel *label = NULL; - QPixmap pixmap; QString thumbnailFileNameNoExt; QLabel *newLabel = NULL; @@ -2779,9 +2812,7 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) label = new ThumbnailLabel(item->widget); - pixmap = QPixmap(QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"); - - label->setPixmap(pixmap); + item->label = label; item->widget->layout()->addWidget(label); @@ -2793,6 +2824,8 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) m_gridLayout->addWidget(item->widget); m_gridItems.append(item); + + loadImageDeferred(item, QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"); } } diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 725196ecb0..888c6ac2f3 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -28,6 +28,8 @@ #include #include #include +#include +#include extern "C" { #include @@ -58,12 +60,19 @@ class QScrollArea; class LoadCoreWindow; class MainWindow; class ThumbnailWidget; +class ThumbnailLabel; class FlowLayout; struct GridItem { + GridItem(); + ThumbnailWidget *widget; + ThumbnailLabel *label; QHash hash; + QImage image; + QPixmap pixmap; + QFutureWatcher imageWatcher; }; class ThumbnailWidget : public QWidget @@ -255,6 +264,7 @@ public: bool setCustomThemeFile(QString filePath); void setCustomThemeString(QString qss); const QString& customThemeString() const; + GridItem* doDeferredImageLoad(GridItem *item, QString path); signals: void thumbnailChanged(const QPixmap &pixmap); @@ -309,6 +319,7 @@ private slots: void onFileBrowserTreeContextMenuRequested(const QPoint &pos); void onPlaylistWidgetContextMenuRequested(const QPoint &pos); void onStopClicked(); + void onDeferredImageLoaded(); private: void setCurrentCoreLabel(); @@ -316,6 +327,7 @@ private: bool isCoreLoaded(); bool isContentLessCore(); void removeGridItems(); + void loadImageDeferred(GridItem *item, QString path); QList > getPlaylistItems(QString pathString); LoadCoreWindow *m_loadCoreWindow; From 8752996018c211f8c986f88e750a9bb34e90d202 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 02:47:40 -0400 Subject: [PATCH 03/23] Qt: add grid zoom slider --- ui/drivers/qt/ui_qt_window.cpp | 45 +++++++++++++++++++++++++++++++--- ui/drivers/ui_qt.h | 6 +++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index be9edf4ca2..396ec9e498 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -499,30 +499,51 @@ MainWindow::MainWindow(QWidget *parent) : ,m_historyPlaylistsItem(NULL) ,m_folderIcon() ,m_customThemeString() - ,m_gridLayout(new FlowLayout()) + ,m_gridLayout(NULL) ,m_gridWidget(new QWidget(this)) ,m_gridScrollArea(new QScrollArea(m_gridWidget)) ,m_gridItems() + ,m_gridLayoutWidget(new QWidget()) + ,m_zoomSlider(NULL) + ,m_lastZoomSliderValue(0) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); QString configDir = QFileInfo(path_get(RARCH_PATH_CONFIG)).dir().absolutePath(); QToolButton *searchResetButton = NULL; - QWidget *gridLayoutWidget = new QWidget(); + QWidget *zoomWidget = new QWidget(); + QHBoxLayout *zoomLayout = new QHBoxLayout(); int i = 0; + m_zoomSlider = new QSlider(Qt::Horizontal, zoomWidget); + + m_zoomSlider->setMinimum(0); + m_zoomSlider->setMaximum(100); + m_zoomSlider->setValue(50); + + m_lastZoomSliderValue = m_zoomSlider->value(); + m_gridWidget->setLayout(new QVBoxLayout()); - gridLayoutWidget->setLayout(m_gridLayout); + m_gridLayout = new FlowLayout(m_gridLayoutWidget); m_gridScrollArea->setAlignment(Qt::AlignCenter); m_gridScrollArea->setFrameShape(QFrame::NoFrame); m_gridScrollArea->setWidgetResizable(true); - m_gridScrollArea->setWidget(gridLayoutWidget); + m_gridScrollArea->setWidget(m_gridLayoutWidget); m_gridWidget->layout()->addWidget(m_gridScrollArea); m_gridWidget->layout()->setAlignment(Qt::AlignCenter); + m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); + + zoomWidget->setLayout(zoomLayout); + zoomLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); + zoomLayout->addWidget(new QLabel("Zoom:")); + zoomLayout->addWidget(m_zoomSlider); + + m_gridWidget->layout()->addWidget(zoomWidget); + m_tableWidget->setAlternatingRowColors(true); m_logWidget->setObjectName("logWidget"); @@ -644,6 +665,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_dirTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileBrowserTreeContextMenuRequested(const QPoint&))); connect(m_listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onPlaylistWidgetContextMenuRequested(const QPoint&))); connect(m_launchWithComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLaunchWithComboBoxIndexChanged(int))); + connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(onZoomValueChanged(int))); /* make sure these use an auto connection so it will be queued if called from a different thread (some facilities in RA log messages from other threads) */ connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection); @@ -687,9 +709,20 @@ MainWindow::~MainWindow() delete m_thumbnailPixmap2; if (m_thumbnailPixmap3) delete m_thumbnailPixmap3; + removeGridItems(); } +void MainWindow::onZoomValueChanged(int value) +{ + foreach(GridItem *item, m_gridItems) + { + item->widget->setFixedSize(QSize(value * 5.12f, value * 5.12f)); + } + + m_lastZoomSliderValue = value; +} + void MainWindow::showWelcomeScreen() { const QString welcomeText = QStringLiteral("" @@ -2853,6 +2886,10 @@ void MainWindow::initContentGridLayout() } else addPlaylistItemsToGrid(path); + + QTimer::singleShot(0, this, [this]() { + m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); + }); } void MainWindow::initContentTableWidget() diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 888c6ac2f3..1c104c3d36 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -29,6 +29,7 @@ #include #include #include +#include #include extern "C" { @@ -57,6 +58,7 @@ class QCheckBox; class QFormLayout; class QStyle; class QScrollArea; +class QSlider; class LoadCoreWindow; class MainWindow; class ThumbnailWidget; @@ -320,6 +322,7 @@ private slots: void onPlaylistWidgetContextMenuRequested(const QPoint &pos); void onStopClicked(); void onDeferredImageLoaded(); + void onZoomValueChanged(int value); private: void setCurrentCoreLabel(); @@ -374,6 +377,9 @@ private: QWidget *m_gridWidget; QScrollArea *m_gridScrollArea; QList m_gridItems; + QWidget *m_gridLayoutWidget; + QSlider *m_zoomSlider; + int m_lastZoomSliderValue; protected: void closeEvent(QCloseEvent *event); From 5fa0d75bd784dae24418942e380f7b3ad637e93d Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 16:12:48 -0400 Subject: [PATCH 04/23] adjust zoom factors --- ui/drivers/qt/ui_qt_window.cpp | 13 ++++++++----- ui/drivers/ui_qt.h | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 396ec9e498..e11af15c70 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -517,8 +517,8 @@ MainWindow::MainWindow(QWidget *parent) : m_zoomSlider = new QSlider(Qt::Horizontal, zoomWidget); - m_zoomSlider->setMinimum(0); - m_zoomSlider->setMaximum(100); + m_zoomSlider->setMinimum(20); + m_zoomSlider->setMaximum(400); m_zoomSlider->setValue(50); m_lastZoomSliderValue = m_zoomSlider->value(); @@ -2887,9 +2887,12 @@ void MainWindow::initContentGridLayout() else addPlaylistItemsToGrid(path); - QTimer::singleShot(0, this, [this]() { - m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); - }); + QTimer::singleShot(0, this, SLOT(onContentGridInited())); +} + +void MainWindow::onContentGridInited() +{ + m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); } void MainWindow::initContentTableWidget() diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 1c104c3d36..6bd22e7821 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -323,6 +323,7 @@ private slots: void onStopClicked(); void onDeferredImageLoaded(); void onZoomValueChanged(int value); + void onContentGridInited(); private: void setCurrentCoreLabel(); From 758d484874fbacf3881d33a93c6daf14dd5e5d87 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 16:31:46 -0400 Subject: [PATCH 05/23] Qt: use primary screen size divided by 8 for grid item size --- ui/drivers/qt/ui_qt_window.cpp | 4 ++++ ui/drivers/ui_qt.cpp | 8 +++++++- ui/drivers/ui_qt.h | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index e11af15c70..c89db53d6d 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2823,6 +2823,8 @@ GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) void MainWindow::addPlaylistItemsToGrid(QString pathString) { QList > items = getPlaylistItems(pathString); + QScreen *screen = qApp->primaryScreen(); + QSize screenSize = screen->size(); settings_t *settings = config_get_ptr(); int i = 0; @@ -2833,12 +2835,14 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) ThumbnailLabel *label = NULL; QString thumbnailFileNameNoExt; QLabel *newLabel = NULL; + QSize thumbnailWidgetSizeHint(screenSize.width() / 8, screenSize.height() / 8); thumbnailFileNameNoExt = hash["label_noext"]; thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); item->hash = hash; item->widget = new ThumbnailWidget(); + item->widget->setSizeHint(thumbnailWidgetSizeHint); item->widget->setFixedSize(item->widget->sizeHint()); item->widget->setLayout(new QVBoxLayout()); item->widget->setStyleSheet("background-color: #555555"); diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index e800e4920b..842bacd396 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -57,6 +57,7 @@ typedef struct ui_companion_qt ThumbnailWidget::ThumbnailWidget(QWidget *parent) : QWidget(parent) + ,m_sizeHint(QSize(256, 256)) { } @@ -80,7 +81,12 @@ void ThumbnailWidget::resizeEvent(QResizeEvent *event) QSize ThumbnailWidget::sizeHint() const { - return QSize(256, 256); + return m_sizeHint; +} + +void ThumbnailWidget::setSizeHint(QSize size) +{ + m_sizeHint = size; } ThumbnailLabel::ThumbnailLabel(QWidget *parent) : diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 6bd22e7821..c771143526 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -83,6 +83,9 @@ class ThumbnailWidget : public QWidget public: ThumbnailWidget(QWidget *parent = 0); QSize sizeHint() const; + void setSizeHint(QSize size); +private: + QSize m_sizeHint; protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); From c42ad9fbb931b19340dc16a78ca4ad1391b06565 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 16:37:36 -0400 Subject: [PATCH 06/23] Qt: respect current zoom level when changing playlists --- ui/drivers/qt/ui_qt_window.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index c89db53d6d..620b5e6232 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2897,6 +2897,8 @@ void MainWindow::initContentGridLayout() void MainWindow::onContentGridInited() { m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); + + onZoomValueChanged(m_zoomSlider->value()); } void MainWindow::initContentTableWidget() From c10e65e99e9e9be3a7a08d1b152f2fea18f076c4 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 16:46:19 -0400 Subject: [PATCH 07/23] Qt: support image playlist in grid layout --- ui/drivers/qt/ui_qt_window.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 620b5e6232..2020601da3 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2836,6 +2836,32 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) QString thumbnailFileNameNoExt; QLabel *newLabel = NULL; QSize thumbnailWidgetSizeHint(screenSize.width() / 8, screenSize.height() / 8); + QByteArray extension; + QString extensionStr; + QString imagePath; + int lastIndex = -1; + + 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 + { + imagePath = QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"; + } thumbnailFileNameNoExt = hash["label_noext"]; thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); @@ -2862,7 +2888,7 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) m_gridLayout->addWidget(item->widget); m_gridItems.append(item); - loadImageDeferred(item, QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"); + loadImageDeferred(item, imagePath); } } From 6cab8cf13933b10326757062efa915efee3ffcbb Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 17:35:13 -0400 Subject: [PATCH 08/23] Qt: fix some grid images not drawing properly --- ui/drivers/qt/ui_qt_window.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 2020601da3..8961ff71a4 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2803,7 +2803,10 @@ void MainWindow::onDeferredImageLoaded() GridItem *item = watcher->result(); if (!item->image.isNull()) + { item->label->setPixmap(QPixmap::fromImage(item->image)); + item->label->update(); + } } void MainWindow::loadImageDeferred(GridItem *item, QString path) From 0f2262f065d76fe62c393a781e6e7d6b9805d744 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 18:55:12 -0400 Subject: [PATCH 09/23] Qt: fix thumbnail image loading --- ui/drivers/qt/ui_qt_window.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 8961ff71a4..5fab5440fc 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2863,12 +2863,11 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) } else { + thumbnailFileNameNoExt = hash["label_noext"]; + thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); imagePath = QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"; } - thumbnailFileNameNoExt = hash["label_noext"]; - thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); - item->hash = hash; item->widget = new ThumbnailWidget(); item->widget->setSizeHint(thumbnailWidgetSizeHint); From e372bb5320335131a7aca07d3c97542e9249fcf5 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 22:10:45 -0400 Subject: [PATCH 10/23] Qt: cut maximum zoom slider value in half --- ui/drivers/qt/ui_qt_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 5fab5440fc..f0d0a8e497 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -518,7 +518,7 @@ MainWindow::MainWindow(QWidget *parent) : m_zoomSlider = new QSlider(Qt::Horizontal, zoomWidget); m_zoomSlider->setMinimum(20); - m_zoomSlider->setMaximum(400); + m_zoomSlider->setMaximum(200); m_zoomSlider->setValue(50); m_lastZoomSliderValue = m_zoomSlider->value(); From fc3251b6a9cdf1650b809899336ae3236ee2dee6 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Thu, 3 May 2018 23:58:15 -0400 Subject: [PATCH 11/23] Qt: use non-linear scale for zoom slider --- ui/drivers/qt/ui_qt_window.cpp | 37 ++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index f0d0a8e497..a54c2363c4 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -54,6 +54,7 @@ extern "C" { #include #include #include +#include } #define TIMER_MSEC 1000 /* periodic timer for gathering statistics */ @@ -85,6 +86,24 @@ enum CoreSelection CORE_SELECTION_LOAD_CORE }; +static double lerp(double x, double y, double a, double b, double d) { + return a + (b - a) * ((double)(d - x) / (double)(y - x)); +} + +/* https://stackoverflow.com/questions/7246622/how-to-create-a-slider-with-a-non-linear-scale */ +static double expScale(double inputValue, double midValue, double maxValue) +{ + double returnValue = 0; + double M = maxValue / midValue; + double C = log(pow(M - 1, 2)); + double B = maxValue / (exp(C) - 1); + double A = -1 * B; + + returnValue = A + B * exp(C * inputValue); + + return returnValue; +} + #ifdef HAVE_LIBRETRODB static void scan_finished_handler(void *task_data, void *user_data, const char *err) { @@ -513,12 +532,15 @@ MainWindow::MainWindow(QWidget *parent) : QToolButton *searchResetButton = NULL; QWidget *zoomWidget = new QWidget(); QHBoxLayout *zoomLayout = new QHBoxLayout(); + QLabel *zoomLabel = new QLabel("Zoom:", zoomWidget); int i = 0; + zoomLabel->setObjectName("zoomLabel"); + m_zoomSlider = new QSlider(Qt::Horizontal, zoomWidget); - m_zoomSlider->setMinimum(20); - m_zoomSlider->setMaximum(200); + m_zoomSlider->setMinimum(0); + m_zoomSlider->setMaximum(100); m_zoomSlider->setValue(50); m_lastZoomSliderValue = m_zoomSlider->value(); @@ -539,7 +561,7 @@ MainWindow::MainWindow(QWidget *parent) : zoomWidget->setLayout(zoomLayout); zoomLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); - zoomLayout->addWidget(new QLabel("Zoom:")); + zoomLayout->addWidget(zoomLabel); zoomLayout->addWidget(m_zoomSlider); m_gridWidget->layout()->addWidget(zoomWidget); @@ -717,7 +739,14 @@ void MainWindow::onZoomValueChanged(int value) { foreach(GridItem *item, m_gridItems) { - item->widget->setFixedSize(QSize(value * 5.12f, value * 5.12f)); + int newSize = 0; + + if (value < 50) + newSize = expScale(lerp(0, 49, 25, 49, value) / 50.0, 102, 256); + else + newSize = expScale(value / 100.0, 256, 1024); + + item->widget->setFixedSize(QSize(newSize, newSize)); } m_lastZoomSliderValue = value; From a082ea7d76edba8b946321efd370899901430f57 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Fri, 4 May 2018 12:05:53 -0400 Subject: [PATCH 12/23] Qt: remove zoom layout margins --- ui/drivers/qt/ui_qt_window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index a54c2363c4..f4e12045b3 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -560,6 +560,7 @@ MainWindow::MainWindow(QWidget *parent) : m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); zoomWidget->setLayout(zoomLayout); + zoomLayout->setContentsMargins(0, 0, 0, 0); zoomLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); zoomLayout->addWidget(zoomLabel); zoomLayout->addWidget(m_zoomSlider); From 8e7e530747e73ea6f80ce6722f2968e123b4a70b Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Sat, 5 May 2018 17:02:03 -0400 Subject: [PATCH 13/23] Qt: fix crash on switching playlists quickly --- ui/drivers/qt/ui_qt_window.cpp | 31 ++++++++++++++++++++++++++++++- ui/drivers/ui_qt.h | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index f4e12045b3..b263402e9b 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -128,6 +128,7 @@ GridItem::GridItem() : ,image() ,pixmap() ,imageWatcher() + ,mutex() { } @@ -738,8 +739,12 @@ MainWindow::~MainWindow() void MainWindow::onZoomValueChanged(int value) { + if (m_gridItems.count() == 0) + return; + foreach(GridItem *item, m_gridItems) { + QMutexLocker lock(&item->mutex); int newSize = 0; if (value < 50) @@ -2816,11 +2821,16 @@ void MainWindow::removeGridItems() if (item) { + item->mutex.lock(); + items.remove(); m_gridLayout->removeWidget(item->widget); delete item->widget; + + item->mutex.unlock(); + delete item; } } @@ -2830,13 +2840,25 @@ void MainWindow::removeGridItems() void MainWindow::onDeferredImageLoaded() { const QFutureWatcher *watcher = static_cast*>(sender()); - GridItem *item = watcher->result(); + GridItem *item = NULL; + + if (!watcher) + return; + + item = watcher->result(); + + if (!item) + return; + + item->mutex.lock(); if (!item->image.isNull()) { item->label->setPixmap(QPixmap::fromImage(item->image)); item->label->update(); } + + item->mutex.unlock(); } void MainWindow::loadImageDeferred(GridItem *item, QString path) @@ -2848,8 +2870,15 @@ void MainWindow::loadImageDeferred(GridItem *item, QString path) GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) { /* this runs in another thread */ + if (!item) + return NULL; + + item->mutex.lock(); + item->image = QImage(path); + item->mutex.unlock(); + return item; } diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index c771143526..91099e6e65 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -75,6 +75,7 @@ struct GridItem QImage image; QPixmap pixmap; QFutureWatcher imageWatcher; + QMutex mutex; }; class ThumbnailWidget : public QWidget From d55b1a0ad40255cef1339a00a78c06348500a7b3 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Sat, 5 May 2018 22:57:29 -0400 Subject: [PATCH 14/23] Qt: only check for pending events of the main thread --- ui/drivers/qt/ui_qt_application.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ui/drivers/qt/ui_qt_application.cpp b/ui/drivers/qt/ui_qt_application.cpp index e8a73a6e9f..f52059395d 100644 --- a/ui/drivers/qt/ui_qt_application.cpp +++ b/ui/drivers/qt/ui_qt_application.cpp @@ -15,6 +15,7 @@ */ #include +#include extern "C" { #include "../../ui_companion_driver.h" @@ -144,13 +145,18 @@ static void* ui_application_qt_initialize(void) static bool ui_application_qt_pending_events(void) { - return QApplication::hasPendingEvents(); + QAbstractEventDispatcher *dispatcher = QApplication::eventDispatcher(); + + if (dispatcher) + return dispatcher->hasPendingEvents(); + + return false; } static void ui_application_qt_process_events(void) { - if(ui_application_qt_pending_events()) - QApplication::processEvents(); + if (ui_application_qt_pending_events()) + QApplication::processEvents(); } static void ui_application_qt_quit(void) From de71a4dee79df72a600d5c3270125b7bc6cbc360 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Sun, 6 May 2018 01:36:47 -0400 Subject: [PATCH 15/23] Qt: general performance improvements, fix crash on switching playlists quickly in grid view, prevent initial loading of grid items from blocking --- ui/drivers/qt/flowlayout.cpp | 187 +++++++++++++---------- ui/drivers/qt/flowlayout.h | 48 +++--- ui/drivers/qt/ui_qt_load_core_window.cpp | 5 +- ui/drivers/qt/ui_qt_window.cpp | 158 ++++++++++++------- ui/drivers/ui_qt.h | 12 +- 5 files changed, 253 insertions(+), 157 deletions(-) diff --git a/ui/drivers/qt/flowlayout.cpp b/ui/drivers/qt/flowlayout.cpp index 0dd3a1f606..6719c13f5c 100644 --- a/ui/drivers/qt/flowlayout.cpp +++ b/ui/drivers/qt/flowlayout.cpp @@ -48,153 +48,184 @@ ** ****************************************************************************/ +/* Original work Copyright (C) 2016 The Qt Company Ltd. + * Modified work Copyright (C) 2018 - Brad Parker + */ #include #include "flowlayout.h" + FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) - : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) { - setContentsMargins(margin, margin, margin, margin); + setContentsMargins(margin, margin, margin, margin); } FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) - : m_hSpace(hSpacing), m_vSpace(vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) { - setContentsMargins(margin, margin, margin, margin); + setContentsMargins(margin, margin, margin, margin); } FlowLayout::~FlowLayout() { - QLayoutItem *item; - while ((item = takeAt(0))) - delete item; + QLayoutItem *item; + + while ((item = takeAt(0))) + delete item; } void FlowLayout::addItem(QLayoutItem *item) { - itemList.append(item); + itemList.append(item); } int FlowLayout::horizontalSpacing() const { - if (m_hSpace >= 0) { - return m_hSpace; - } else { - return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - } + 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); - } + if (m_vSpace >= 0) + return m_vSpace; + else + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); } int FlowLayout::count() const { - return itemList.size(); + return itemList.size(); } -QLayoutItem *FlowLayout::itemAt(int index) const +QLayoutItem* FlowLayout::itemAt(int index) const { - return itemList.value(index); + return itemList.value(index); } -QLayoutItem *FlowLayout::takeAt(int index) +QLayoutItem* FlowLayout::takeAt(int index) { - if (index >= 0 && index < itemList.size()) - return itemList.takeAt(index); - else - return 0; + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + else + return NULL; } Qt::Orientations FlowLayout::expandingDirections() const { - return 0; + return 0; } bool FlowLayout::hasHeightForWidth() const { - return true; + return true; } int FlowLayout::heightForWidth(int width) const { - int height = doLayout(QRect(0, 0, width, 0), true); - return height; + int height = doLayout(QRect(0, 0, width, 0), true); + return height; } void FlowLayout::setGeometry(const QRect &rect) { - QLayout::setGeometry(rect); - doLayout(rect, false); + QLayout::setGeometry(rect); + doLayout(rect, false); } QSize FlowLayout::sizeHint() const { - return minimumSize(); + return minimumSize(); } QSize FlowLayout::minimumSize() const { - QSize size; - QLayoutItem *item; - foreach (item, itemList) - size = size.expandedTo(item->minimumSize()); + QSize size; + int i = 0; - size += QSize(2*margin(), 2*margin()); - return size; + 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 { - int left, top, right, bottom; - getContentsMargins(&left, &top, &right, &bottom); - QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); - int x = effectiveRect.x(); - int y = effectiveRect.y(); - int lineHeight = 0; + QRect effectiveRect; + int left = 0, top = 0, right = 0, bottom = 0; + int x = 0; + int y = 0; + int lineHeight = 0; + int i = 0; - QLayoutItem *item; - foreach (item, itemList) { - QWidget *wid = item->widget(); - int spaceX = horizontalSpacing(); - if (spaceX == -1) - spaceX = wid->style()->layoutSpacing( - QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - int spaceY = verticalSpacing(); - if (spaceY == -1) - spaceY = wid->style()->layoutSpacing( - QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - int 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; - } + getContentsMargins(&left, &top, &right, &bottom); + effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + x = effectiveRect.x(); + y = effectiveRect.y(); - if (!testOnly) - item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + if (itemList.isEmpty()) + return y + lineHeight - rect.y() + bottom; - x = nextX; - lineHeight = qMax(lineHeight, item->sizeHint().height()); - } - 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 { - QObject *parent = this->parent(); - if (!parent) { - return -1; - } else if (parent->isWidgetType()) { - QWidget *pw = static_cast(parent); - return pw->style()->pixelMetric(pm, 0, pw); - } else { - return static_cast(parent)->spacing(); - } + const QObject *parentObj = parent(); + + if (!parentObj) + return -1; + else if (parentObj->isWidgetType()) + { + const QWidget *pw = static_cast(parentObj); + return pw->style()->pixelMetric(pm, 0, pw); + } + else + return static_cast(parentObj)->spacing(); } diff --git a/ui/drivers/qt/flowlayout.h b/ui/drivers/qt/flowlayout.h index 436ba5e4c6..282a43df94 100644 --- a/ui/drivers/qt/flowlayout.h +++ b/ui/drivers/qt/flowlayout.h @@ -48,7 +48,13 @@ ** ****************************************************************************/ -/* bparker: Removed C++11 override keyword from original source */ +/* 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 @@ -60,30 +66,30 @@ class FlowLayout : public QLayout { 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(); + 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 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); private: - int doLayout(const QRect &rect, bool testOnly) const; - int smartSpacing(QStyle::PixelMetric pm) const; + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; - QList itemList; - int m_hSpace; - int m_vSpace; + QVector itemList; + int m_hSpace; + int m_vSpace; }; #endif // FLOWLAYOUT_H diff --git a/ui/drivers/qt/ui_qt_load_core_window.cpp b/ui/drivers/qt/ui_qt_load_core_window.cpp index 65da0381fc..00f29f38de 100644 --- a/ui/drivers/qt/ui_qt_load_core_window.cpp +++ b/ui/drivers/qt/ui_qt_load_core_window.cpp @@ -266,8 +266,11 @@ void LoadCoreWindow::initCoreList(const QStringList &extensionFilters) if (rowsToHide.size() != m_table->rowCount()) { - foreach (const int &row, rowsToHide) + int i = 0; + + for (i = 0; i < rowsToHide.count() && rowsToHide.count() > 0; i++) { + const int &row = rowsToHide.at(i); m_table->setRowHidden(row, true); } } diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index b263402e9b..00da8a0ac6 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -200,7 +200,7 @@ void CoreInfoDialog::showCoreInfo() int row = 0; int rowCount = m_formLayout->rowCount(); int i = 0; - QList > infoList = m_mainwindow->getCoreInfo(); + QVector > infoList = m_mainwindow->getCoreInfo(); if (rowCount > 0) { @@ -526,6 +526,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_gridLayoutWidget(new QWidget()) ,m_zoomSlider(NULL) ,m_lastZoomSliderValue(0) + ,m_pendingItemUpdates() { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -707,7 +708,7 @@ MainWindow::MainWindow(QWidget *parent) : qApp->processEvents(); QTimer::singleShot(0, this, SLOT(onBrowserStartClicked())); - for (i = 0; i < m_listWidget->count(); i++) + for (i = 0; i < m_listWidget->count() && m_listWidget->count() > 0; i++) { /* select the first non-hidden row */ if (!m_listWidget->isRowHidden(i)) @@ -737,22 +738,27 @@ MainWindow::~MainWindow() removeGridItems(); } +inline void MainWindow::calcGridItemSize(GridItem *item, int zoomValue) +{ + QMutexLocker lock(&item->mutex); + int newSize = 0; + + if (zoomValue < 50) + newSize = expScale(lerp(0, 49, 25, 49, zoomValue) / 50.0, 102, 256); + else + newSize = expScale(zoomValue / 100.0, 256, 1024); + + item->widget->setFixedSize(QSize(newSize, newSize)); +} + void MainWindow::onZoomValueChanged(int value) { - if (m_gridItems.count() == 0) - return; + int i = 0; - foreach(GridItem *item, m_gridItems) + for (i = 0; i < m_gridItems.count() && m_gridItems.count() > 0; i++) { - QMutexLocker lock(&item->mutex); - int newSize = 0; - - if (value < 50) - newSize = expScale(lerp(0, 49, 25, 49, value) / 50.0, 102, 256); - else - newSize = expScale(value / 100.0, 256, 1024); - - item->widget->setFixedSize(QSize(newSize, newSize)); + GridItem *item = m_gridItems.at(i); + calcGridItemSize(item, value); } m_lastZoomSliderValue = value; @@ -948,20 +954,17 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) menu->addAction(hideAction.data()); - if (m_listWidget->count() > 0) + for (j = 0; j < m_listWidget->count() && m_listWidget->count() > 0; j++) { - for (j = 0; j < m_listWidget->count(); j++) - { - QListWidgetItem *item = m_listWidget->item(j); - bool hidden = m_listWidget->isItemHidden(item); + QListWidgetItem *item = m_listWidget->item(j); + bool hidden = m_listWidget->isItemHidden(item); - if (hidden) - { - QAction *action = hiddenPlaylistsMenu->addAction(item->text()); - action->setProperty("row", j); - action->setProperty("core_path", item->data(Qt::UserRole).toString()); - foundHiddenPlaylist = true; - } + if (hidden) + { + QAction *action = hiddenPlaylistsMenu->addAction(item->text()); + action->setProperty("row", j); + action->setProperty("core_path", item->data(Qt::UserRole).toString()); + foundHiddenPlaylist = true; } } @@ -986,17 +989,28 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) core_info_get_list(&core_info_list); - for (i = 0; i < core_info_list->count; i++) + for (i = 0; i < core_info_list->count && core_info_list->count > 0; i++) { const core_info_t *core = &core_info_list->list[i]; coreList[core->core_name] = core; } - foreach (const QString &key, coreList.keys()) { - const core_info_t *core = coreList.value(key); - QAction *action = associateMenu->addAction(core->core_name); - action->setProperty("core_path", core->path); + QMapIterator coreListIterator(coreList); + + while (coreListIterator.hasNext()) + { + QString key; + const core_info_t *core = NULL; + QAction *action = NULL; + + coreListIterator.next(); + + key = coreListIterator.key(); + core = coreList.value(key); + action = associateMenu->addAction(core->core_name); + action->setProperty("core_path", core->path); + } } menu->addMenu(associateMenu.data()); @@ -1180,6 +1194,7 @@ void MainWindow::reloadPlaylists() QDir playlistDir(settings->paths.directory_playlist); QString currentPlaylistPath; QStringList hiddenPlaylists = m_settings->value("hidden_playlists").toStringList(); + int i = 0; currentItem = m_listWidget->currentItem(); @@ -1230,9 +1245,10 @@ void MainWindow::reloadPlaylists() if (hiddenPlaylists.contains(QFileInfo(settings->paths.path_content_video_history).fileName())) m_listWidget->setRowHidden(m_listWidget->row(videoPlaylistsItem), true); - foreach (QString file, m_playlistFiles) + for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) { QListWidgetItem *item = NULL; + const QString &file = m_playlistFiles.at(i); QString fileDisplayName = file; QString fileName = file; bool hasIcon = false; @@ -1270,7 +1286,6 @@ void MainWindow::reloadPlaylists() if (firstItem) { - int i = 0; bool found = false; for (i = 0; i < m_listWidget->count(); i++) @@ -1318,7 +1333,7 @@ void MainWindow::onGotLogMessage(const QString &msg) void MainWindow::onLaunchWithComboBoxIndexChanged(int) { - QList > infoList = getCoreInfo(); + QVector > infoList = getCoreInfo(); QString coreInfoText; QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); @@ -1423,9 +1438,9 @@ void MainWindow::setTheme(Theme theme) } } -QList > MainWindow::getCoreInfo() +QVector > MainWindow::getCoreInfo() { - QList > infoList; + QVector > infoList; QHash currentCore = getSelectedCore(); core_info_list_t *core_info_list = NULL; const core_info_t *core_info = NULL; @@ -1850,7 +1865,7 @@ QHash MainWindow::getSelectedCore() } case CORE_SELECTION_PLAYLIST_DEFAULT: { - QList > cores; + QVector > cores; int i = 0; if (!contentItem || contentHash["db_name"].isEmpty()) @@ -1982,7 +1997,7 @@ void MainWindow::onRunClicked() } case CORE_SELECTION_PLAYLIST_DEFAULT: { - QList > cores = getPlaylistDefaultCores(); + QVector > cores = getPlaylistDefaultCores(); int i = 0; for (i = 0; i < cores.count(); i++) @@ -2047,13 +2062,13 @@ ViewOptionsDialog* MainWindow::viewOptionsDialog() return m_viewOptionsDialog; } -QList > MainWindow::getPlaylistDefaultCores() +QVector > MainWindow::getPlaylistDefaultCores() { settings_t *settings = config_get_ptr(); struct string_list *playlists = string_split(settings->arrays.playlist_names, ";"); struct string_list *cores = string_split(settings->arrays.playlist_cores, ";"); unsigned i = 0; - QList > coreList; + QVector > coreList; if (!playlists || !cores) { @@ -2164,7 +2179,7 @@ void MainWindow::setCoreActions() if (!hash["db_name"].isEmpty()) { - QList > defaultCores = getPlaylistDefaultCores(); + QVector > defaultCores = getPlaylistDefaultCores(); int i = 0; if (defaultCores.count() > 0) @@ -2406,6 +2421,7 @@ void MainWindow::onViewClosedDocksAboutToShow() QMenu *menu = qobject_cast(sender()); QList dockWidgets; bool found = false; + int i = 0; if (!menu) return; @@ -2420,8 +2436,10 @@ void MainWindow::onViewClosedDocksAboutToShow() return; } - foreach (QDockWidget *dock, dockWidgets) + for (i = 0; i < dockWidgets.count() && dockWidgets.count() > 0; i++) { + const QDockWidget *dock = dockWidgets.at(i); + if (!dock->isVisible()) { QAction *action = menu->addAction(dock->property("menu_text").toString(), this, SLOT(onShowHiddenDockWidgetAction())); @@ -2431,9 +2449,7 @@ void MainWindow::onViewClosedDocksAboutToShow() } if (!found) - { menu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NONE)); - } } void MainWindow::onShowHiddenDockWidgetAction() @@ -2826,6 +2842,7 @@ void MainWindow::removeGridItems() items.remove(); m_gridLayout->removeWidget(item->widget); + m_pendingItemUpdates.removeAll(item); delete item->widget; @@ -2854,13 +2871,39 @@ void MainWindow::onDeferredImageLoaded() if (!item->image.isNull()) { - item->label->setPixmap(QPixmap::fromImage(item->image)); - item->label->update(); + m_pendingItemUpdates.append(item); + QTimer::singleShot(0, this, SLOT(onPendingItemUpdates())); } item->mutex.unlock(); } +void MainWindow::onPendingItemUpdates() +{ + QMutableListIterator list(m_pendingItemUpdates); + + while (list.hasNext()) + { + GridItem *item = list.next(); + + if (!item) + continue; + + onUpdateGridItemPixmapFromImage(item); + + list.remove(); + } +} + +void MainWindow::onUpdateGridItemPixmapFromImage(GridItem *item) +{ + if (!item) + return; + + item->label->setPixmap(QPixmap::fromImage(item->image)); + item->label->update(); +} + void MainWindow::loadImageDeferred(GridItem *item, QString path) { connect(&item->imageWatcher, SIGNAL(finished()), this, SLOT(onDeferredImageLoaded()), Qt::QueuedConnection); @@ -2882,13 +2925,14 @@ GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) return item; } -void MainWindow::addPlaylistItemsToGrid(QString pathString) +void MainWindow::addPlaylistItemsToGrid(const QString &pathString) { - QList > items = getPlaylistItems(pathString); + QVector > items = getPlaylistItems(pathString); QScreen *screen = qApp->primaryScreen(); QSize screenSize = screen->size(); settings_t *settings = config_get_ptr(); int i = 0; + int zoomValue = m_zoomSlider->value(); for (i = 0; i < items.count(); i++) { @@ -2938,6 +2982,8 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) item->label = label; + calcGridItemSize(item, zoomValue); + item->widget->layout()->addWidget(label); newLabel = new QLabel(hash.value("label"), item->widget); @@ -2950,6 +2996,8 @@ void MainWindow::addPlaylistItemsToGrid(QString pathString) m_gridItems.append(item); loadImageDeferred(item, imagePath); + + qApp->processEvents(); } } @@ -2969,9 +3017,11 @@ void MainWindow::initContentGridLayout() { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); + int i = 0; - foreach (QString playlist, m_playlistFiles) + for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) { + const QString &playlist = m_playlistFiles.at(i); addPlaylistItemsToGrid(playlistDir.absoluteFilePath(playlist)); } } @@ -3018,9 +3068,11 @@ void MainWindow::initContentTableWidget() { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); + int i = 0; - foreach (QString playlist, m_playlistFiles) + for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) { + const QString &playlist = m_playlistFiles.at(i); addPlaylistItemsToTable(playlistDir.absoluteFilePath(playlist)); } } @@ -3047,10 +3099,10 @@ void MainWindow::initContentTableWidget() onSearchEnterPressed(); } -QList > MainWindow::getPlaylistItems(QString pathString) +QVector > MainWindow::getPlaylistItems(QString pathString) { QByteArray pathArray; - QList > items; + QVector > items; const char *pathData = NULL; playlist_t *playlist = NULL; unsigned playlistSize = 0; @@ -3118,7 +3170,7 @@ QList > MainWindow::getPlaylistItems(QString pathString) void MainWindow::addPlaylistItemsToTable(QString pathString) { - QList > items = getPlaylistItems(pathString); + QVector > items = getPlaylistItems(pathString); int i = 0; int oldRowCount = m_tableWidget->rowCount(); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 91099e6e65..5344e2e842 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -256,10 +256,10 @@ public: QToolButton* runPushButton(); QToolButton* stopPushButton(); QTabWidget* browserAndPlaylistTabWidget(); - QList > getPlaylistDefaultCores(); + QVector > getPlaylistDefaultCores(); ViewOptionsDialog* viewOptionsDialog(); QSettings* settings(); - QList > getCoreInfo(); + QVector > getCoreInfo(); void setTheme(Theme theme = THEME_SYSTEM_DEFAULT); Theme theme(); Theme getThemeFromString(QString themeString); @@ -315,7 +315,7 @@ private slots: void onSearchEnterPressed(); void onSearchLineEditEdited(const QString &text); void addPlaylistItemsToTable(QString path); - void addPlaylistItemsToGrid(QString path); + void addPlaylistItemsToGrid(const QString &path); void onContentItemDoubleClicked(QTableWidgetItem *item); void onCoreLoadWindowClosed(); void onTabWidgetIndexChanged(int index); @@ -328,6 +328,8 @@ private slots: void onDeferredImageLoaded(); void onZoomValueChanged(int value); void onContentGridInited(); + void onUpdateGridItemPixmapFromImage(GridItem *item); + void onPendingItemUpdates(); private: void setCurrentCoreLabel(); @@ -336,7 +338,8 @@ private: bool isContentLessCore(); void removeGridItems(); void loadImageDeferred(GridItem *item, QString path); - QList > getPlaylistItems(QString pathString); + void calcGridItemSize(GridItem *item, int zoomValue); + QVector > getPlaylistItems(QString pathString); LoadCoreWindow *m_loadCoreWindow; QTimer *m_timer; @@ -385,6 +388,7 @@ private: QWidget *m_gridLayoutWidget; QSlider *m_zoomSlider; int m_lastZoomSliderValue; + QList m_pendingItemUpdates; protected: void closeEvent(QCloseEvent *event); From 2a784ae1bc5c73b5836c958a65f0af003f2c81f1 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Sun, 6 May 2018 20:50:54 -0400 Subject: [PATCH 16/23] Qt: remove mutex, defer loading of grid items into layout, use guarded pointers for exec() operations, misc cleanup --- Makefile.common | 3 +- ui/drivers/qt/flowlayout.cpp | 16 +++++++-- ui/drivers/qt/flowlayout.h | 8 +++++ ui/drivers/qt/ui_qt_window.cpp | 65 +++++++++++++++++----------------- ui/drivers/ui_qt.h | 1 - 5 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Makefile.common b/Makefile.common index 44d9185717..2152bae548 100644 --- a/Makefile.common +++ b/Makefile.common @@ -336,7 +336,8 @@ OBJ += ui/drivers/ui_qt.o \ ui/drivers/qt/flowlayout.o MOC_HEADERS += ui/drivers/ui_qt.h \ - ui/drivers/qt/ui_qt_load_core_window.h + ui/drivers/qt/ui_qt_load_core_window.h \ + ui/drivers/qt/flowlayout.h DEFINES += $(QT5CORE_CFLAGS) $(QT5GUI_CFLAGS) $(QT5WIDGETS_CFLAGS) $(QT5CONCURRENT_CFLAGS) -DHAVE_MAIN #DEFINES += $(QT5WEBENGINE_CFLAGS) diff --git a/ui/drivers/qt/flowlayout.cpp b/ui/drivers/qt/flowlayout.cpp index 6719c13f5c..8f8ba5333a 100644 --- a/ui/drivers/qt/flowlayout.cpp +++ b/ui/drivers/qt/flowlayout.cpp @@ -60,6 +60,8 @@ 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(QWidget*)), this, SLOT(onAddWidgetDeferred(QWidget*)), Qt::QueuedConnection); } FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) @@ -70,9 +72,9 @@ FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) FlowLayout::~FlowLayout() { - QLayoutItem *item; + QLayoutItem *item = NULL; - while ((item = takeAt(0))) + while ((item = takeAt(0)) != NULL) delete item; } @@ -229,3 +231,13 @@ int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const else return static_cast(parentObj)->spacing(); } + +void FlowLayout::addWidgetDeferred(QWidget *widget) +{ + emit signalAddWidgetDeferred(widget); +} + +void FlowLayout::onAddWidgetDeferred(QWidget *widget) +{ + addWidget(widget); +} diff --git a/ui/drivers/qt/flowlayout.h b/ui/drivers/qt/flowlayout.h index 282a43df94..84dc349d51 100644 --- a/ui/drivers/qt/flowlayout.h +++ b/ui/drivers/qt/flowlayout.h @@ -65,6 +65,7 @@ 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); @@ -82,6 +83,13 @@ public: void setGeometry(const QRect &rect); QSize sizeHint() const; QLayoutItem* takeAt(int index); + void addWidgetDeferred(QWidget *widget); + +signals: + void signalAddWidgetDeferred(QWidget *widget); + +private slots: + void onAddWidgetDeferred(QWidget *widget); private: int doLayout(const QRect &rect, bool testOnly) const; diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 00da8a0ac6..6a4d26e759 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -111,6 +111,10 @@ static void scan_finished_handler(void *task_data, void *user_data, const char * menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST; menu_environ.data = NULL; + (void)task_data; + (void)user_data; + (void)err; + menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ); if (!ui_window.qtWindow->settings()->value("scan_finish_confirm", true).toBool()) @@ -128,7 +132,6 @@ GridItem::GridItem() : ,image() ,pixmap() ,imageWatcher() - ,mutex() { } @@ -740,7 +743,6 @@ MainWindow::~MainWindow() inline void MainWindow::calcGridItemSize(GridItem *item, int zoomValue) { - QMutexLocker lock(&item->mutex); int newSize = 0; if (zoomValue < 50) @@ -850,12 +852,12 @@ void MainWindow::setCustomThemeString(QString qss) bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality) { - QScopedPointer msgBoxPtr; + QPointer > msgBoxPtr; QMessageBox *msgBox = NULL; QCheckBox *checkBox = NULL; - msgBoxPtr.reset(new QMessageBox(this)); - msgBox = msgBoxPtr.data(); + msgBoxPtr.data()->reset(new QMessageBox(this)); + msgBox = msgBoxPtr.data()->data(); checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); msgBox->setWindowModality(modality); @@ -891,6 +893,9 @@ bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowM msgBox->setText(msg); msgBox->exec(); + if (!msgBoxPtr) + return true; + if (checkBox->isChecked()) return false; @@ -904,7 +909,7 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&) QScopedPointer associateMenu; QScopedPointer hiddenPlaylistsMenu; QScopedPointer hideAction; - QAction *selectedAction = NULL; + QPointer selectedAction; QPoint cursorPos = QCursor::pos(); QListWidgetItem *selectedItem = m_listWidget->itemAt(m_listWidget->viewport()->mapFromGlobal(cursorPos)); QDir playlistDir(settings->paths.directory_playlist); @@ -1103,7 +1108,7 @@ end: void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&) { #ifdef HAVE_LIBRETRODB - QAction *action = NULL; + QPointer action; QList actions; QScopedPointer scanAction; QDir dir; @@ -1153,6 +1158,8 @@ void MainWindow::onGotStatusMessage(QString msg, unsigned priority, unsigned dur QScreen *screen = qApp->primaryScreen(); QStatusBar *status = statusBar(); + Q_UNUSED(priority) + if (msg.isEmpty()) return; @@ -2831,23 +2838,21 @@ void MainWindow::removeGridItems() { QMutableListIterator items(m_gridItems); + m_pendingItemUpdates.clear(); + while (items.hasNext()) { GridItem *item = items.next(); if (item) { - item->mutex.lock(); + item->imageWatcher.waitForFinished(); items.remove(); m_gridLayout->removeWidget(item->widget); - m_pendingItemUpdates.removeAll(item); delete item->widget; - - item->mutex.unlock(); - delete item; } } @@ -2867,15 +2872,14 @@ void MainWindow::onDeferredImageLoaded() if (!item) return; - item->mutex.lock(); - - if (!item->image.isNull()) + if (m_gridItems.contains(item)) { - m_pendingItemUpdates.append(item); - QTimer::singleShot(0, this, SLOT(onPendingItemUpdates())); + if (!item->image.isNull()) + { + m_pendingItemUpdates.append(item); + QTimer::singleShot(0, this, SLOT(onPendingItemUpdates())); + } } - - item->mutex.unlock(); } void MainWindow::onPendingItemUpdates() @@ -2889,7 +2893,8 @@ void MainWindow::onPendingItemUpdates() if (!item) continue; - onUpdateGridItemPixmapFromImage(item); + if (m_gridItems.contains(item)) + onUpdateGridItemPixmapFromImage(item); list.remove(); } @@ -2900,6 +2905,9 @@ void MainWindow::onUpdateGridItemPixmapFromImage(GridItem *item) if (!item) return; + if (!m_gridItems.contains(item)) + return; + item->label->setPixmap(QPixmap::fromImage(item->image)); item->label->update(); } @@ -2916,12 +2924,11 @@ GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) if (!item) return NULL; - item->mutex.lock(); - + /* While we are indeed writing across thread boundaries here, the image is never accessed until after + * its thread finishes, and the item is never deleted without first waiting for the thread to finish. + */ item->image = QImage(path); - item->mutex.unlock(); - return item; } @@ -2992,12 +2999,10 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) item->widget->layout()->addWidget(newLabel); qobject_cast(item->widget->layout())->setStretchFactor(label, 1); - m_gridLayout->addWidget(item->widget); + m_gridLayout->addWidgetDeferred(item->widget); m_gridItems.append(item); loadImageDeferred(item, imagePath); - - qApp->processEvents(); } } @@ -3178,12 +3183,6 @@ void MainWindow::addPlaylistItemsToTable(QString pathString) for (i = 0; i < items.count(); i++) { - const char *path = NULL; - const char *label = NULL; - const char *core_path = NULL; - const char *core_name = NULL; - const char *crc32 = NULL; - const char *db_name = NULL; QTableWidgetItem *labelItem = NULL; const QHash &hash = items.at(i); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 5344e2e842..7952fb3c14 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -75,7 +75,6 @@ struct GridItem QImage image; QPixmap pixmap; QFutureWatcher imageWatcher; - QMutex mutex; }; class ThumbnailWidget : public QWidget From faf52937c5eca732daacbcc95973df2fc04ecf4b Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Mon, 7 May 2018 00:07:12 -0400 Subject: [PATCH 17/23] Qt: buildfix --- ui/drivers/qt/ui_qt_window.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 6a4d26e759..5883a98f06 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -852,12 +852,12 @@ void MainWindow::setCustomThemeString(QString qss) bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowModality modality) { - QPointer > msgBoxPtr; + QPointer msgBoxPtr; QMessageBox *msgBox = NULL; QCheckBox *checkBox = NULL; - msgBoxPtr.data()->reset(new QMessageBox(this)); - msgBox = msgBoxPtr.data()->data(); + msgBoxPtr = new QMessageBox(this); + msgBox = msgBoxPtr.data(); checkBox = new QCheckBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DONT_SHOW_AGAIN), msgBox); msgBox->setWindowModality(modality); From 649216c700778622edfa71ae287125d69018760c Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Mon, 7 May 2018 02:36:00 -0400 Subject: [PATCH 18/23] Qt: add view menu option to change between icon/list views --- intl/msg_hash_ja.h | 8 +++ intl/msg_hash_us.h | 8 +++ msg_hash.h | 4 ++ ui/drivers/qt/ui_qt_themes.h | 14 ++++- ui/drivers/qt/ui_qt_window.cpp | 109 ++++++++++++++++++++++++++++----- ui/drivers/ui_qt.cpp | 70 +++++++++++++++++---- ui/drivers/ui_qt.h | 14 ++++- 7 files changed, 198 insertions(+), 29 deletions(-) diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index ceb35d4a95..ff74abff6e 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3497,6 +3497,14 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST, "ファイルは存在しません。") MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SUGGEST_LOADED_CORE_FIRST, "ロードしたコアを最初に優先する") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ZOOM, + "ズーム") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_VIEW, + "表示") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS, + "アイコン") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST, + "一覧") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_OVERRIDE_OPTIONS, "Configuration Override options") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_OVERRIDE_OPTIONS, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 29a33bc8e8..5c10866c78 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3646,6 +3646,14 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST, "File does not exist.") MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_SUGGEST_LOADED_CORE_FIRST, "Suggest loaded core first") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_ZOOM, + "Zoom") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_VIEW, + "View") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS, + "Icons") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST, + "List") MSG_HASH(MENU_ENUM_LABEL_VALUE_QUICK_MENU_OVERRIDE_OPTIONS, "Configuration Override options") MSG_HASH(MENU_ENUM_SUBLABEL_QUICK_MENU_OVERRIDE_OPTIONS, diff --git a/msg_hash.h b/msg_hash.h index 833b518760..1120be6a37 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1846,6 +1846,10 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_QT_FILE_IS_EMPTY, MENU_ENUM_LABEL_VALUE_QT_FILE_READ_OPEN_FAILED, MENU_ENUM_LABEL_VALUE_QT_FILE_DOES_NOT_EXIST, + MENU_ENUM_LABEL_VALUE_QT_ZOOM, + MENU_ENUM_LABEL_VALUE_QT_VIEW, + MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS, + MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST, MSG_LAST }; diff --git a/ui/drivers/qt/ui_qt_themes.h b/ui/drivers/qt/ui_qt_themes.h index 058a073bc7..46051401fb 100644 --- a/ui/drivers/qt/ui_qt_themes.h +++ b/ui/drivers/qt/ui_qt_themes.h @@ -1,7 +1,15 @@ #include /* %1 is a placeholder for palette(highlight) or the equivalent chosen by the user */ -static const QString qt_theme_default_stylesheet = QStringLiteral(""); +static const QString qt_theme_default_stylesheet = QStringLiteral("" + "QPushButton[flat=\"true\"] {\n" + " min-height:20px;\n" + " min-width:80px;\n" + " padding:1px 3px 1px 3px;\n" + " background-color: transparent;\n" + " border: 1px solid #ddd;\n" + "}\n" +); static const QString qt_theme_dark_stylesheet = QStringLiteral("" "QWidget {\n" @@ -232,6 +240,10 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral("" " border:1px solid %1;\n" " border-radius:4px;\n" "}\n" + "QPushButton[flat=\"true\"] {\n" + " background-color: transparent;\n" + " border: 1px solid #ddd;\n" + "}\n" "QRadioButton::indicator {\n" " width:18px;\n" " height:18px;\n" diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 5883a98f06..6cac031241 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -530,6 +530,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_zoomSlider(NULL) ,m_lastZoomSliderValue(0) ,m_pendingItemUpdates() + ,m_viewType(VIEW_TYPE_LIST) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -537,9 +538,21 @@ MainWindow::MainWindow(QWidget *parent) : QToolButton *searchResetButton = NULL; QWidget *zoomWidget = new QWidget(); QHBoxLayout *zoomLayout = new QHBoxLayout(); - QLabel *zoomLabel = new QLabel("Zoom:", zoomWidget); + QLabel *zoomLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ZOOM), zoomWidget); + QPushButton *viewTypePushButton = new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW), zoomWidget); + QMenu *viewTypeMenu = new QMenu(viewTypePushButton); + QAction *viewTypeIconsAction = NULL; + QAction *viewTypeListAction = NULL; int i = 0; + viewTypePushButton->setObjectName("viewTypePushButton"); + viewTypePushButton->setFlat(true); + + viewTypeIconsAction = viewTypeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS)); + viewTypeListAction = viewTypeMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST)); + + viewTypePushButton->setMenu(viewTypeMenu); + zoomLabel->setObjectName("zoomLabel"); m_zoomSlider = new QSlider(Qt::Horizontal, zoomWidget); @@ -569,6 +582,7 @@ MainWindow::MainWindow(QWidget *parent) : zoomLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); zoomLayout->addWidget(zoomLabel); zoomLayout->addWidget(m_zoomSlider); + zoomLayout->addWidget(viewTypePushButton); m_gridWidget->layout()->addWidget(zoomWidget); @@ -688,12 +702,13 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo())); connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked())); connect(m_stopPushButton, SIGNAL(clicked()), this, SLOT(onStopClicked())); - connect(m_browserAndPlaylistTabWidget, SIGNAL(currentChanged(int)), this, SLOT(onTabWidgetIndexChanged(int))); connect(m_dirTree, SIGNAL(itemsSelected(QModelIndexList)), this, SLOT(onTreeViewItemsSelected(QModelIndexList))); connect(m_dirTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onFileBrowserTreeContextMenuRequested(const QPoint&))); connect(m_listWidget, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onPlaylistWidgetContextMenuRequested(const QPoint&))); connect(m_launchWithComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onLaunchWithComboBoxIndexChanged(int))); connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(onZoomValueChanged(int))); + connect(viewTypeIconsAction, SIGNAL(triggered()), this, SLOT(onIconViewClicked())); + connect(viewTypeListAction, SIGNAL(triggered()), this, SLOT(onListViewClicked())); /* make sure these use an auto connection so it will be queued if called from a different thread (some facilities in RA log messages from other threads) */ connect(this, SIGNAL(gotLogMessage(const QString&)), this, SLOT(onGotLogMessage(const QString&)), Qt::AutoConnection); @@ -711,16 +726,6 @@ MainWindow::MainWindow(QWidget *parent) : qApp->processEvents(); QTimer::singleShot(0, this, SLOT(onBrowserStartClicked())); - for (i = 0; i < m_listWidget->count() && m_listWidget->count() > 0; i++) - { - /* select the first non-hidden row */ - if (!m_listWidget->isRowHidden(i)) - { - m_listWidget->setCurrentRow(i); - break; - } - } - m_searchLineEdit->setFocus(); m_loadCoreWindow->setWindowModality(Qt::ApplicationModal); @@ -741,6 +746,18 @@ MainWindow::~MainWindow() removeGridItems(); } +void MainWindow::onIconViewClicked() +{ + setCurrentViewType(VIEW_TYPE_ICONS); + onCurrentListItemChanged(m_listWidget->currentItem(), NULL); +} + +void MainWindow::onListViewClicked() +{ + setCurrentViewType(VIEW_TYPE_LIST); + onCurrentListItemChanged(m_listWidget->currentItem(), NULL); +} + inline void MainWindow::calcGridItemSize(GridItem *item, int zoomValue) { int newSize = 0; @@ -2625,16 +2642,58 @@ void MainWindow::resizeThumbnails(bool one, bool two, bool three) } } +void MainWindow::setCurrentViewType(ViewType viewType) +{ + m_viewType = viewType; + + switch (viewType) + { + case VIEW_TYPE_ICONS: + { + m_tableWidget->hide(); + m_gridWidget->show(); + break; + } + case VIEW_TYPE_LIST: + default: + { + m_gridWidget->hide(); + m_tableWidget->show(); + break; + } + } +} + +MainWindow::ViewType MainWindow::getCurrentViewType() +{ + return m_viewType; +} + void MainWindow::onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { + ViewType viewType = getCurrentViewType(); + Q_UNUSED(current) Q_UNUSED(previous) if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) != msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) return; - //initContentTableWidget(); - initContentGridLayout(); + switch (viewType) + { + case VIEW_TYPE_ICONS: + { + initContentGridLayout(); + break; + } + case VIEW_TYPE_LIST: + default: + { + initContentTableWidget(); + break; + } + } + setCoreActions(); } @@ -3041,6 +3100,8 @@ void MainWindow::onContentGridInited() m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); onZoomValueChanged(m_zoomSlider->value()); + + onSearchEnterPressed(); } void MainWindow::initContentTableWidget() @@ -3213,6 +3274,24 @@ QSettings* MainWindow::settings() return m_settings; } +QString MainWindow::getCurrentViewTypeString() +{ + switch (m_viewType) + { + case VIEW_TYPE_ICONS: + { + return QStringLiteral("icons"); + } + case VIEW_TYPE_LIST: + default: + { + return QStringLiteral("list"); + } + } + + return QStringLiteral("list"); +} + void MainWindow::closeEvent(QCloseEvent *event) { if (m_settings->value("save_geometry", false).toBool()) @@ -3222,6 +3301,8 @@ void MainWindow::closeEvent(QCloseEvent *event) if (m_settings->value("save_last_tab", false).toBool()) m_settings->setValue("last_tab", m_browserAndPlaylistTabWidget->currentIndex()); + m_settings->setValue("view_type", getCurrentViewTypeString()); + QMainWindow::closeEvent(event); } diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index 842bacd396..5229843ab9 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -237,6 +237,8 @@ static void* ui_companion_qt_init(void) QAction *exitAction = NULL; QComboBox *launchWithComboBox = NULL; QSettings *qsettings = NULL; + QListWidget *listWidget = NULL; + int i = 0; if (!handle) return NULL; @@ -257,6 +259,8 @@ static void* ui_companion_qt_init(void) mainwindow->setWindowTitle("RetroArch"); mainwindow->setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks | GROUPED_DRAGGING); + listWidget = mainwindow->playlistListWidget(); + widget = new QWidget(mainwindow); widget->setObjectName("tableWidget"); @@ -264,8 +268,6 @@ static void* ui_companion_qt_init(void) layout->addWidget(mainwindow->contentTableWidget()); layout->addWidget(mainwindow->contentGridWidget()); - mainwindow->contentTableWidget()->hide(); - widget->setLayout(layout); mainwindow->setCentralWidget(widget); @@ -295,6 +297,10 @@ static void* ui_companion_qt_init(void) QObject::connect(viewClosedDocksMenu, SIGNAL(aboutToShow()), mainwindow, SLOT(onViewClosedDocksAboutToShow())); + viewMenu->addSeparator(); + viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS), mainwindow, SLOT(onIconViewClicked())); + viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST), mainwindow, SLOT(onListViewClicked())); + viewMenu->addSeparator(); viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS), mainwindow->viewOptionsDialog(), SLOT(showDialog())); playlistWidget = new QWidget(); @@ -464,17 +470,6 @@ static void* ui_companion_qt_init(void) if (qsettings->contains("dock_positions")) mainwindow->restoreState(qsettings->value("dock_positions").toByteArray()); - if (qsettings->contains("save_last_tab")) - { - if (qsettings->contains("last_tab")) - { - int lastTabIndex = qsettings->value("last_tab", 0).toInt(); - - if (lastTabIndex >= 0 && browserAndPlaylistTabWidget->count() > lastTabIndex) - browserAndPlaylistTabWidget->setCurrentIndex(lastTabIndex); - } - } - if (qsettings->contains("theme")) { QString themeStr = qsettings->value("theme").toString(); @@ -492,6 +487,55 @@ static void* ui_companion_qt_init(void) else mainwindow->setTheme(); + if (qsettings->contains("view_type")) + { + QString viewType = qsettings->value("view_type", "list").toString(); + + if (viewType == "list") + mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST); + else if (viewType == "icons") + mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_ICONS); + else + mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST); + } + 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. + */ + QObject::connect(browserAndPlaylistTabWidget, SIGNAL(currentChanged(int)), mainwindow, SLOT(onTabWidgetIndexChanged(int))); + + /* setting the last tab must come after setting the view type */ + if (qsettings->contains("save_last_tab")) + { + if (qsettings->contains("last_tab")) + { + int lastTabIndex = qsettings->value("last_tab", 0).toInt(); + + if (lastTabIndex >= 0 && browserAndPlaylistTabWidget->count() > lastTabIndex) + { + browserAndPlaylistTabWidget->setCurrentIndex(lastTabIndex); + mainwindow->onTabWidgetIndexChanged(lastTabIndex); + } + } + } + else + { + browserAndPlaylistTabWidget->setCurrentIndex(0); + mainwindow->onTabWidgetIndexChanged(0); + } + + for (i = 0; i < listWidget->count() && listWidget->count() > 0; i++) + { + /* select the first non-hidden row */ + if (!listWidget->isRowHidden(i)) + { + listWidget->setCurrentRow(i); + break; + } + } + return handle; } diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 7952fb3c14..a035b355b1 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -226,6 +226,12 @@ class MainWindow : public QMainWindow Q_OBJECT public: + enum ViewType + { + VIEW_TYPE_ICONS, + VIEW_TYPE_LIST + }; + enum Theme { THEME_SYSTEM_DEFAULT, @@ -270,6 +276,7 @@ public: void setCustomThemeString(QString qss); const QString& customThemeString() const; GridItem* doDeferredImageLoad(GridItem *item, QString path); + void setCurrentViewType(ViewType viewType); signals: void thumbnailChanged(const QPixmap &pixmap); @@ -303,6 +310,9 @@ public slots: void deferReloadPlaylists(); void onGotReloadPlaylists(); void showWelcomeScreen(); + void onIconViewClicked(); + void onListViewClicked(); + void onTabWidgetIndexChanged(int index); private slots: void onLoadCoreClicked(const QStringList &extensionFilters = QStringList()); @@ -317,7 +327,6 @@ private slots: void addPlaylistItemsToGrid(const QString &path); void onContentItemDoubleClicked(QTableWidgetItem *item); void onCoreLoadWindowClosed(); - void onTabWidgetIndexChanged(int index); void onTreeViewItemsSelected(QModelIndexList selectedIndexes); void onSearchResetClicked(); void onLaunchWithComboBoxIndexChanged(int index); @@ -339,6 +348,8 @@ private: void loadImageDeferred(GridItem *item, QString path); void calcGridItemSize(GridItem *item, int zoomValue); QVector > getPlaylistItems(QString pathString); + QString getCurrentViewTypeString(); + ViewType getCurrentViewType(); LoadCoreWindow *m_loadCoreWindow; QTimer *m_timer; @@ -388,6 +399,7 @@ private: QSlider *m_zoomSlider; int m_lastZoomSliderValue; QList m_pendingItemUpdates; + ViewType m_viewType; protected: void closeEvent(QCloseEvent *event); From 03e0510fc46ff1438f5a40829e887399789ae3e1 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Mon, 7 May 2018 14:29:54 -0400 Subject: [PATCH 19/23] Qt: use guarded pointer for adding grid items to layout, in case playlist is changed (and hence the items get deleted) before they are all added --- ui/drivers/qt/flowlayout.cpp | 13 +++++++++---- ui/drivers/qt/flowlayout.h | 8 +++++--- ui/drivers/qt/ui_qt_window.cpp | 13 ++++++++----- ui/drivers/ui_qt.h | 13 +++++++++---- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/ui/drivers/qt/flowlayout.cpp b/ui/drivers/qt/flowlayout.cpp index 8f8ba5333a..b970e10b10 100644 --- a/ui/drivers/qt/flowlayout.cpp +++ b/ui/drivers/qt/flowlayout.cpp @@ -55,13 +55,14 @@ #include #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(QWidget*)), this, SLOT(onAddWidgetDeferred(QWidget*)), Qt::QueuedConnection); + connect(this, SIGNAL(signalAddWidgetDeferred(QPointer)), this, SLOT(onAddWidgetDeferred(QPointer)), Qt::QueuedConnection); } FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) @@ -226,18 +227,22 @@ int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const else if (parentObj->isWidgetType()) { const QWidget *pw = static_cast(parentObj); - return pw->style()->pixelMetric(pm, 0, pw); + return pw->style()->pixelMetric(pm, NULL, pw); } else return static_cast(parentObj)->spacing(); } -void FlowLayout::addWidgetDeferred(QWidget *widget) +void FlowLayout::addWidgetDeferred(QPointer widget) { emit signalAddWidgetDeferred(widget); } -void FlowLayout::onAddWidgetDeferred(QWidget *widget) +void FlowLayout::onAddWidgetDeferred(QPointer 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); } diff --git a/ui/drivers/qt/flowlayout.h b/ui/drivers/qt/flowlayout.h index 84dc349d51..c9d11e7653 100644 --- a/ui/drivers/qt/flowlayout.h +++ b/ui/drivers/qt/flowlayout.h @@ -63,6 +63,8 @@ #include #include +class ThumbnailWidget; + class FlowLayout : public QLayout { Q_OBJECT @@ -83,13 +85,13 @@ public: void setGeometry(const QRect &rect); QSize sizeHint() const; QLayoutItem* takeAt(int index); - void addWidgetDeferred(QWidget *widget); + void addWidgetDeferred(QPointer widget); signals: - void signalAddWidgetDeferred(QWidget *widget); + void signalAddWidgetDeferred(QPointer widget); private slots: - void onAddWidgetDeferred(QWidget *widget); + void onAddWidgetDeferred(QPointer widget); private: int doLayout(const QRect &rect, bool testOnly) const; diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 6cac031241..56ed1c463b 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -126,7 +126,8 @@ static void scan_finished_handler(void *task_data, void *user_data, const char * #endif GridItem::GridItem() : - widget(NULL) + QObject() + ,widget(NULL) ,label(NULL) ,hash() ,image() @@ -545,6 +546,8 @@ MainWindow::MainWindow(QWidget *parent) : QAction *viewTypeListAction = NULL; int i = 0; + qRegisterMetaType >("ThumbnailWidget"); + viewTypePushButton->setObjectName("viewTypePushButton"); viewTypePushButton->setFlat(true); @@ -2895,13 +2898,13 @@ void MainWindow::removeGridItems() { if (m_gridItems.count() > 0) { - QMutableListIterator items(m_gridItems); + QMutableVectorIterator > items(m_gridItems); m_pendingItemUpdates.clear(); while (items.hasNext()) { - GridItem *item = items.next(); + QPointer item = items.next(); if (item) { @@ -3003,8 +3006,8 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) for (i = 0; i < items.count(); i++) { const QHash &hash = items.at(i); - GridItem *item = new GridItem(); - ThumbnailLabel *label = NULL; + QPointer item (new GridItem()); + QPointer label; QString thumbnailFileNameNoExt; QLabel *newLabel = NULL; QSize thumbnailWidgetSizeHint(screenSize.width() / 8, screenSize.height() / 8); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index a035b355b1..42a404faca 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -31,6 +31,7 @@ #include #include #include +#include extern "C" { #include @@ -65,12 +66,14 @@ class ThumbnailWidget; class ThumbnailLabel; class FlowLayout; -struct GridItem +class GridItem : public QObject { + Q_OBJECT +public: GridItem(); - ThumbnailWidget *widget; - ThumbnailLabel *label; + QPointer widget; + QPointer label; QHash hash; QImage image; QPixmap pixmap; @@ -394,7 +397,7 @@ private: FlowLayout *m_gridLayout; QWidget *m_gridWidget; QScrollArea *m_gridScrollArea; - QList m_gridItems; + QVector > m_gridItems; QWidget *m_gridLayoutWidget; QSlider *m_zoomSlider; int m_lastZoomSliderValue; @@ -406,6 +409,8 @@ protected: void keyPressEvent(QKeyEvent *event); }; +Q_DECLARE_METATYPE(QPointer) + RETRO_BEGIN_DECLS typedef struct ui_application_qt From 8c6bc4a6780f0bf89a34387e709a986cbac71d82 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Mon, 28 May 2018 18:14:36 -0400 Subject: [PATCH 20/23] Qt: update UI after loading every 25 grid items, keep grid from mixing up content from multiple playlists if the user switches to a new one before one is finished loading --- ui/drivers/qt/ui_qt_window.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 56ed1c463b..f4db64cb15 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2999,6 +2999,7 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) QVector > items = getPlaylistItems(pathString); 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(); @@ -3006,7 +3007,7 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) for (i = 0; i < items.count(); i++) { const QHash &hash = items.at(i); - QPointer item (new GridItem()); + QPointer item; QPointer label; QString thumbnailFileNameNoExt; QLabel *newLabel = NULL; @@ -3016,6 +3017,14 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) QString imagePath; int lastIndex = -1; + if (m_listWidget->currentItem() != currentItem) + { + /* user changed the current playlist before we finished loading... abort */ + break; + } + + item = new GridItem(); + lastIndex = hash["path"].lastIndexOf('.'); if (lastIndex >= 0) @@ -3065,6 +3074,9 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) m_gridItems.append(item); loadImageDeferred(item, imagePath); + + if (i % 25 == 0) + qApp->processEvents(); } } From f78337e02e66eb66819be2f1b6faccbe72b78ae4 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Sun, 22 Jul 2018 22:05:57 -0400 Subject: [PATCH 21/23] Qt: add progress bar for grid view loading --- intl/msg_hash_ja.h | 2 ++ intl/msg_hash_us.h | 2 ++ msg_hash.h | 1 + ui/drivers/qt/ui_qt_window.cpp | 65 +++++++++++++++++++++++++++++++--- ui/drivers/ui_qt.h | 5 ++- 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index cfc87db199..2756c4d4bf 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -3630,3 +3630,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SUSTAINED_PERFORMANCE_MODE, "パフォーマンス維持モード") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_MPV_SUPPORT, "mpv対応") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PROGRESS, + "進行状況:") diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index bbe66247e9..b96c7ea606 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -3788,3 +3788,5 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SUSTAINED_PERFORMANCE_MODE, "Sustained Performance Mode") MSG_HASH(MENU_ENUM_LABEL_VALUE_SYSTEM_INFO_MPV_SUPPORT, "mpv support") +MSG_HASH(MENU_ENUM_LABEL_VALUE_QT_PROGRESS, + "Progress:") diff --git a/msg_hash.h b/msg_hash.h index 24a687cd2b..63e952cc07 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1875,6 +1875,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_QT_VIEW, MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS, MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_LIST, + MENU_ENUM_LABEL_VALUE_QT_PROGRESS, MENU_LABEL(MIDI_INPUT), MENU_LABEL(MIDI_OUTPUT), diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 57d0ba1fc3..83bdd6b8e8 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -532,6 +532,8 @@ MainWindow::MainWindow(QWidget *parent) : ,m_lastZoomSliderValue(0) ,m_pendingItemUpdates() ,m_viewType(VIEW_TYPE_LIST) + ,m_gridProgressBar(NULL) + ,m_gridProgressWidget(NULL) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -544,10 +546,16 @@ MainWindow::MainWindow(QWidget *parent) : QMenu *viewTypeMenu = new QMenu(viewTypePushButton); QAction *viewTypeIconsAction = NULL; QAction *viewTypeListAction = NULL; + QHBoxLayout *gridProgressLayout = new QHBoxLayout(); + QLabel *gridProgressLabel = NULL; + QHBoxLayout *gridFooterLayout = NULL; int i = 0; qRegisterMetaType >("ThumbnailWidget"); + m_gridProgressWidget = new QWidget(); + gridProgressLabel = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_PROGRESS), m_gridProgressWidget); + viewTypePushButton->setObjectName("viewTypePushButton"); viewTypePushButton->setFlat(true); @@ -556,6 +564,12 @@ MainWindow::MainWindow(QWidget *parent) : viewTypePushButton->setMenu(viewTypeMenu); + gridProgressLabel->setObjectName("gridProgressLabel"); + + m_gridProgressBar = new QProgressBar(m_gridProgressWidget); + + m_gridProgressBar->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); + zoomLabel->setObjectName("zoomLabel"); m_zoomSlider = new QSlider(Qt::Horizontal, zoomWidget); @@ -563,6 +577,7 @@ MainWindow::MainWindow(QWidget *parent) : m_zoomSlider->setMinimum(0); m_zoomSlider->setMaximum(100); m_zoomSlider->setValue(50); + m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); m_lastZoomSliderValue = m_zoomSlider->value(); @@ -578,16 +593,28 @@ MainWindow::MainWindow(QWidget *parent) : m_gridWidget->layout()->addWidget(m_gridScrollArea); m_gridWidget->layout()->setAlignment(Qt::AlignCenter); - m_zoomSlider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred)); + m_gridProgressWidget->setLayout(gridProgressLayout); + gridProgressLayout->setContentsMargins(0, 0, 0, 0); + gridProgressLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); + gridProgressLayout->addWidget(gridProgressLabel); + gridProgressLayout->addWidget(m_gridProgressBar); + + m_gridWidget->layout()->addWidget(m_gridProgressWidget); zoomWidget->setLayout(zoomLayout); zoomLayout->setContentsMargins(0, 0, 0, 0); - zoomLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); zoomLayout->addWidget(zoomLabel); zoomLayout->addWidget(m_zoomSlider); zoomLayout->addWidget(viewTypePushButton); - m_gridWidget->layout()->addWidget(zoomWidget); + gridFooterLayout = new QHBoxLayout(); + gridFooterLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); + gridFooterLayout->addWidget(m_gridProgressWidget); + gridFooterLayout->addWidget(zoomWidget); + + static_cast(m_gridWidget->layout())->addLayout(gridFooterLayout); + + m_gridProgressWidget->hide(); m_tableWidget->setAlternatingRowColors(true); @@ -2994,7 +3021,7 @@ GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) return item; } -void MainWindow::addPlaylistItemsToGrid(const QString &pathString) +void MainWindow::addPlaylistItemsToGrid(const QString &pathString, bool setProgress) { QVector > items = getPlaylistItems(pathString); QScreen *screen = qApp->primaryScreen(); @@ -3004,6 +3031,14 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) int i = 0; int zoomValue = m_zoomSlider->value(); + /* setProgress means we are resetting the range of the progress bar as we are only loading a single playlist. If false, just increment by 1 since instead we're loading multiple playlists and we only track the progress of each entire playlist that is loaded, instead of every single entry. */ + if (setProgress) + { + m_gridProgressBar->setMinimum(0); + m_gridProgressBar->setMaximum(items.count() - 1); + m_gridProgressBar->setValue(0); + } + for (i = 0; i < items.count(); i++) { const QHash &hash = items.at(i); @@ -3077,7 +3112,17 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString) if (i % 25 == 0) qApp->processEvents(); + + if (setProgress) + m_gridProgressBar->setValue(i); } + + if (!setProgress) + m_gridProgressBar->setValue(m_gridProgressBar->value() + 1); + + /* 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(); } void MainWindow::initContentGridLayout() @@ -3088,6 +3133,11 @@ void MainWindow::initContentGridLayout() if (!item) return; + m_gridProgressBar->setMinimum(0); + m_gridProgressBar->setMaximum(0); + m_gridProgressBar->setValue(0); + m_gridProgressWidget->show(); + removeGridItems(); path = item->data(Qt::UserRole).toString(); @@ -3098,10 +3148,15 @@ void MainWindow::initContentGridLayout() QDir playlistDir(settings->paths.directory_playlist); int i = 0; + m_gridProgressBar->setMinimum(0); + m_gridProgressBar->setMaximum(m_playlistFiles.count() - 1); + m_gridProgressBar->setValue(0); + for (i = 0; i < m_playlistFiles.count() && m_playlistFiles.count() > 0; i++) { const QString &playlist = m_playlistFiles.at(i); - addPlaylistItemsToGrid(playlistDir.absoluteFilePath(playlist)); + m_gridProgressBar->setValue(i); + addPlaylistItemsToGrid(playlistDir.absoluteFilePath(playlist), false); } } else diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 42a404faca..20d8f99b76 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -32,6 +32,7 @@ #include #include #include +#include extern "C" { #include @@ -327,7 +328,7 @@ private slots: void onSearchEnterPressed(); void onSearchLineEditEdited(const QString &text); void addPlaylistItemsToTable(QString path); - void addPlaylistItemsToGrid(const QString &path); + void addPlaylistItemsToGrid(const QString &path, bool setProgress = true); void onContentItemDoubleClicked(QTableWidgetItem *item); void onCoreLoadWindowClosed(); void onTreeViewItemsSelected(QModelIndexList selectedIndexes); @@ -403,6 +404,8 @@ private: int m_lastZoomSliderValue; QList m_pendingItemUpdates; ViewType m_viewType; + QProgressBar *m_gridProgressBar; + QWidget *m_gridProgressWidget; protected: void closeEvent(QCloseEvent *event); From 1293447aab9afbd33996aac49fc035c1d7685206 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Mon, 23 Jul 2018 09:44:24 -0400 Subject: [PATCH 22/23] Qt: hide progress properly when done showing "all playlists" if user only has a single playlist --- ui/drivers/qt/ui_qt_window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 83bdd6b8e8..9ea00b13ff 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -3055,6 +3055,7 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString, bool setProgr if (m_listWidget->currentItem() != currentItem) { /* user changed the current playlist before we finished loading... abort */ + m_gridProgressWidget->hide(); break; } @@ -3117,7 +3118,7 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString, bool setProgr m_gridProgressBar->setValue(i); } - if (!setProgress) + if (!setProgress && m_gridProgressBar->value() < m_gridProgressBar->maximum()) m_gridProgressBar->setValue(m_gridProgressBar->value() + 1); /* 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. */ From c8a814ae40225d305b44e28fa81574b83c7f7e21 Mon Sep 17 00:00:00 2001 From: Brad Parker Date: Mon, 23 Jul 2018 11:17:58 -0400 Subject: [PATCH 23/23] Qt: hook up grid view click/doubleclick events --- ui/drivers/qt/ui_qt_window.cpp | 163 +++++++++++++++++++++++---------- ui/drivers/ui_qt.cpp | 33 +++++-- ui/drivers/ui_qt.h | 15 ++- 3 files changed, 153 insertions(+), 58 deletions(-) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 9ea00b13ff..4b770c723b 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -534,6 +534,8 @@ MainWindow::MainWindow(QWidget *parent) : ,m_viewType(VIEW_TYPE_LIST) ,m_gridProgressBar(NULL) ,m_gridProgressWidget(NULL) + ,m_currentGridHash() + ,m_lastViewType(m_viewType) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -776,6 +778,34 @@ MainWindow::~MainWindow() removeGridItems(); } +void MainWindow::onGridItemClicked() +{ + QHash hash; + ThumbnailWidget *w = static_cast(sender()); + + if (!w) + return; + + hash = w->property("hash").value >(); + + m_currentGridHash = hash; + + currentItemChanged(hash); +} + +void MainWindow::onGridItemDoubleClicked() +{ + QHash hash; + ThumbnailWidget *w = static_cast(sender()); + + if (!w) + return; + + hash = w->property("hash").value >(); + + loadContent(hash); +} + void MainWindow::onIconViewClicked() { setCurrentViewType(VIEW_TYPE_ICONS); @@ -1896,9 +1926,12 @@ QHash MainWindow::getSelectedCore() QHash coreHash; QHash contentHash; QTableWidgetItem *contentItem = m_tableWidget->currentItem(); + ViewType viewType = getCurrentViewType(); - if (contentItem) + if (viewType == VIEW_TYPE_LIST && contentItem) contentHash = contentItem->data(Qt::UserRole).value >(); + else if (viewType == VIEW_TYPE_ICONS) + contentHash = m_currentGridHash; switch(coreSelection) { @@ -1949,12 +1982,19 @@ QHash MainWindow::getSelectedCore() return coreHash; } -void MainWindow::onRunClicked() +/* the hash typically has the following keys: +path - absolute path to the content file +core_path - absolute path to the core, or "DETECT" to ask the user +db_name - the display name of the rdb database this content is from +label - the display name of the content, usually comes from the database +crc32 - an upper-case, 8 byte string representation of the hex CRC32 checksum (e.g. ABCDEF12) followed by "|crc" +core_name - the display name of the core, or "DETECT" if unknown +label_noext - the display name of the content that is guaranteed not to contain a file extension +*/ +void MainWindow::loadContent(const QHash &contentHash) { #ifdef HAVE_MENU content_ctx_info_t content_info; - QHash contentHash; - QTableWidgetItem *item = m_tableWidget->currentItem(); QByteArray corePathArray; QByteArray contentPathArray; QByteArray contentLabelArray; @@ -1964,63 +2004,50 @@ void MainWindow::onRunClicked() QVariantMap coreMap = m_launchWithComboBox->currentData(Qt::UserRole).value(); CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); - if (!item) - return; - if (m_pendingRun) coreSelection = CORE_SELECTION_CURRENT; - contentHash = item->data(Qt::UserRole).value >(); - if (coreSelection == CORE_SELECTION_ASK) { - QTableWidgetItem *item = m_tableWidget->currentItem(); QStringList extensionFilters; - if (item) + if (contentHash.contains("path")) { - QHash hash; + int lastIndex = contentHash["path"].lastIndexOf('.'); + QString extensionStr; + QByteArray pathArray = contentHash["path"].toUtf8(); + const char *pathData = pathArray.constData(); - hash = item->data(Qt::UserRole).value >(); - - if (hash.contains("path")) + if (lastIndex >= 0) { - int lastIndex = hash["path"].lastIndexOf('.'); - QString extensionStr; - QByteArray pathArray = hash["path"].toUtf8(); - const char *pathData = pathArray.constData(); + extensionStr = contentHash["path"].mid(lastIndex + 1); - if (lastIndex >= 0) + if (!extensionStr.isEmpty()) { - extensionStr = hash["path"].mid(lastIndex + 1); - - if (!extensionStr.isEmpty()) - { - extensionFilters.append(extensionStr.toLower()); - } + extensionFilters.append(extensionStr.toLower()); } + } - if (path_is_compressed_file(pathData)) + if (path_is_compressed_file(pathData)) + { + unsigned i = 0; + struct string_list *list = file_archive_get_file_list(pathData, NULL); + + if (list) { - unsigned i = 0; - struct string_list *list = file_archive_get_file_list(pathData, NULL); - - if (list) + if (list->size > 0) { - if (list->size > 0) + for (i = 0; i < list->size; i++) { - for (i = 0; i < list->size; i++) - { - const char *filePath = list->elems[i].data; - const char *extension = path_get_extension(filePath); + const char *filePath = list->elems[i].data; + const char *extension = path_get_extension(filePath); - if (!extensionFilters.contains(extension, Qt::CaseInsensitive)) - extensionFilters.append(extension); - } + if (!extensionFilters.contains(extension, Qt::CaseInsensitive)) + extensionFilters.append(extension); } - - string_list_free(list); } + + string_list_free(list); } } } @@ -2096,6 +2123,25 @@ void MainWindow::onRunClicked() #endif } +void MainWindow::onRunClicked() +{ +#ifdef HAVE_MENU + QTableWidgetItem *item = m_tableWidget->currentItem(); + ViewType viewType = getCurrentViewType(); + QHash contentHash; + + if (!item) + return; + + if (viewType == VIEW_TYPE_LIST) + contentHash = item->data(Qt::UserRole).value >(); + else if (viewType == VIEW_TYPE_ICONS) + contentHash = m_currentGridHash; + + loadContent(contentHash); +#endif +} + bool MainWindow::isContentLessCore() { rarch_system_info_t *system = runloop_get_system_info(); @@ -2164,6 +2210,7 @@ void MainWindow::setCoreActions() { QTableWidgetItem *currentContentItem = m_tableWidget->currentItem(); QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem(); + ViewType viewType = getCurrentViewType(); QHash hash; m_launchWithComboBox->clear(); @@ -2182,8 +2229,10 @@ void MainWindow::setCoreActions() m_launchWithComboBox->addItem(m_currentCore, QVariant::fromValue(comboBoxMap)); } - if (currentContentItem) + if (viewType == VIEW_TYPE_LIST && currentContentItem) hash = currentContentItem->data(Qt::UserRole).value >(); + else if (viewType == VIEW_TYPE_ICONS) + hash = m_currentGridHash; if (m_browserAndPlaylistTabWidget->tabText(m_browserAndPlaylistTabWidget->currentIndex()) == msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_TAB_PLAYLISTS)) { @@ -2341,6 +2390,9 @@ void MainWindow::onTabWidgetIndexChanged(int index) { QModelIndex index = m_dirTree->currentIndex(); + /* force list view for file browser, will set it back to whatever the user had when switching back to playlist tab */ + setCurrentViewType(VIEW_TYPE_LIST); + m_tableWidget->clear(); m_tableWidget->setColumnCount(0); m_tableWidget->setRowCount(0); @@ -2355,6 +2407,9 @@ void MainWindow::onTabWidgetIndexChanged(int index) { QListWidgetItem *item = m_listWidget->currentItem(); + if (m_lastViewType != getCurrentViewType()) + setCurrentViewType(m_lastViewType); + m_tableWidget->clear(); m_tableWidget->setColumnCount(0); m_tableWidget->setRowCount(0); @@ -2544,19 +2599,25 @@ void MainWindow::onSearchEnterPressed() void MainWindow::onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *) { - settings_t *settings = config_get_ptr(); QHash hash; - QString label; - QString playlist_name; - QByteArray extension; - QString extensionStr; - int lastIndex = -1; if (!current) return; hash = current->data(Qt::UserRole).value >(); + currentItemChanged(hash); +} + +void MainWindow::currentItemChanged(const QHash &hash) +{ + settings_t *settings = config_get_ptr(); + QString label; + QString playlist_name; + QByteArray extension; + QString extensionStr; + int lastIndex = -1; + label = hash["label_noext"]; label.replace(m_fileSanitizerRegex, "_"); @@ -2674,6 +2735,7 @@ void MainWindow::resizeThumbnails(bool one, bool two, bool three) void MainWindow::setCurrentViewType(ViewType viewType) { + m_lastViewType = m_viewType; m_viewType = viewType; switch (viewType) @@ -3092,6 +3154,11 @@ void MainWindow::addPlaylistItemsToGrid(const QString &pathString, bool setProgr item->widget->setLayout(new QVBoxLayout()); item->widget->setStyleSheet("background-color: #555555"); + item->widget->setProperty("hash", QVariant::fromValue >(hash)); + + connect(item->widget, SIGNAL(mouseDoubleClicked()), this, SLOT(onGridItemDoubleClicked())); + connect(item->widget, SIGNAL(mousePressed()), this, SLOT(onGridItemClicked())); + label = new ThumbnailLabel(item->widget); item->label = label; diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index 5229843ab9..ba0644b285 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -61,17 +61,31 @@ ThumbnailWidget::ThumbnailWidget(QWidget *parent) : { } +void ThumbnailWidget::mousePressEvent(QMouseEvent *event) +{ + QWidget::mousePressEvent(event); + + emit mousePressed(); +} + +void ThumbnailWidget::mouseDoubleClickEvent(QMouseEvent *event) +{ + QWidget::mouseDoubleClickEvent(event); + + emit mouseDoubleClicked(); +} + void ThumbnailWidget::paintEvent(QPaintEvent *event) { - QStyleOption o; - QPainter p; - o.initFrom(this); - p.begin(this); - style()->drawPrimitive( - QStyle::PE_Widget, &o, &p, this); - p.end(); + QStyleOption o; + QPainter p; + o.initFrom(this); + p.begin(this); + style()->drawPrimitive( + QStyle::PE_Widget, &o, &p, this); + p.end(); - QWidget::paintEvent(event); + QWidget::paintEvent(event); } void ThumbnailWidget::resizeEvent(QResizeEvent *event) @@ -497,6 +511,9 @@ static void* ui_companion_qt_init(void) mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_ICONS); else mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST); + + /* we set it to the same thing a second time so that m_lastViewType is also equal to the startup view type */ + mainwindow->setCurrentViewType(mainwindow->getCurrentViewType()); } else mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 20d8f99b76..941a49ba5e 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -88,11 +88,16 @@ public: ThumbnailWidget(QWidget *parent = 0); QSize sizeHint() const; void setSizeHint(QSize size); +signals: + void mouseDoubleClicked(); + void mousePressed(); private: QSize m_sizeHint; protected: void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); }; class ThumbnailLabel : public QWidget @@ -281,6 +286,8 @@ public: const QString& customThemeString() const; GridItem* doDeferredImageLoad(GridItem *item, QString path); void setCurrentViewType(ViewType viewType); + QString getCurrentViewTypeString(); + ViewType getCurrentViewType(); signals: void thumbnailChanged(const QPixmap &pixmap); @@ -300,6 +307,7 @@ public slots: void onShowHiddenDockWidgetAction(); void setCoreActions(); void onRunClicked(); + void loadContent(const QHash &contentHash); void onStartCoreClicked(); void onTableWidgetEnterPressed(); void selectBrowserDir(QString path); @@ -325,6 +333,7 @@ private slots: void onCoreLoaded(); void onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); + void currentItemChanged(const QHash &hash); void onSearchEnterPressed(); void onSearchLineEditEdited(const QString &text); void addPlaylistItemsToTable(QString path); @@ -342,6 +351,8 @@ private slots: void onContentGridInited(); void onUpdateGridItemPixmapFromImage(GridItem *item); void onPendingItemUpdates(); + void onGridItemDoubleClicked(); + void onGridItemClicked(); private: void setCurrentCoreLabel(); @@ -352,8 +363,6 @@ private: void loadImageDeferred(GridItem *item, QString path); void calcGridItemSize(GridItem *item, int zoomValue); QVector > getPlaylistItems(QString pathString); - QString getCurrentViewTypeString(); - ViewType getCurrentViewType(); LoadCoreWindow *m_loadCoreWindow; QTimer *m_timer; @@ -406,6 +415,8 @@ private: ViewType m_viewType; QProgressBar *m_gridProgressBar; QWidget *m_gridProgressWidget; + QHash m_currentGridHash; + ViewType m_lastViewType; protected: void closeEvent(QCloseEvent *event);