From e981afcca86d606607e37e8761e2d10a74e62255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20L=C3=B3pez?= Date: Sat, 20 Oct 2018 21:26:13 +0200 Subject: [PATCH 01/13] Fix msvc2017 project --- .gitignore | 4 ++ pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj | 64 ++++--------------- 2 files changed, 18 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index 28188376c9..1773b66a74 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,7 @@ retroarch_switch.lst retroarch_switch.nacp retroarch_switch.nro retroarch_switch.nso +/pkg/msvc/x64/Debug QT +/pkg/msvc/msvc-2017/x64/Debug QT +/pkg/msvc/msvc-2017/MetaObjects +/.vs diff --git a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj index f228bad8bf..bb74c23f60 100644 --- a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj +++ b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj @@ -281,7 +281,7 @@ true - $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ + $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -290,7 +290,7 @@ true - $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ + $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(CG_LIB64_PATH);$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -349,7 +349,7 @@ false - $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ + $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -358,7 +358,7 @@ false - $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ + $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(CG_LIB64_PATH);$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -423,7 +423,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -444,7 +444,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -506,7 +506,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -527,7 +527,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -604,7 +604,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -630,7 +630,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -707,7 +707,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -733,7 +733,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -759,49 +759,13 @@ CompileAsC CompileAsC - + /bigobj %(AdditionalOptions) /bigobj %(AdditionalOptions) /bigobj %(AdditionalOptions) /bigobj %(AdditionalOptions) - - true - true - false - false - true - false - false - true - true - true - false - false - true - false - false - true - - - true - true - false - false - true - false - false - true - true - true - false - false - true - false - false - true - @@ -908,4 +872,4 @@ - + \ No newline at end of file From a868ef29e85cbd85c1f1835826d07b8605406de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20L=C3=B3pez?= Date: Wed, 12 Dec 2018 20:45:19 +0100 Subject: [PATCH 02/13] Qt: Implement custom playlist model and grid view. Only load images when they become visible and cache them. Add option to change thumbnail type displayed in grid view. Add option to change thumbnail cache limit. --- Makefile.common | 4 +- griffin/griffin_cpp.cpp | 4 +- intl/msg_hash_us.h | 10 +- msg_hash.h | 2 + ui/drivers/qt/filedropwidget.cpp | 7 +- ui/drivers/qt/filedropwidget.h | 1 + ui/drivers/qt/flowlayout.cpp | 248 ------ ui/drivers/qt/flowlayout.h | 105 --- ui/drivers/qt/gridview.cpp | 392 +++++++++ ui/drivers/qt/gridview.h | 74 ++ ui/drivers/qt/playlist.cpp | 419 ++++++---- ui/drivers/qt/playlistthumbnaildownload.cpp | 18 +- ui/drivers/qt/thumbnaildownload.cpp | 2 + ui/drivers/qt/thumbnailpackdownload.cpp | 7 +- ui/drivers/qt/ui_qt_themes.h | 10 +- ui/drivers/qt/ui_qt_window.cpp | 838 +++++--------------- ui/drivers/qt/viewoptionsdialog.cpp | 33 +- ui/drivers/qt/viewoptionsdialog.h | 3 + ui/drivers/ui_qt.cpp | 27 +- ui/drivers/ui_qt.h | 130 +-- 20 files changed, 1105 insertions(+), 1229 deletions(-) delete mode 100644 ui/drivers/qt/flowlayout.cpp delete mode 100644 ui/drivers/qt/flowlayout.h create mode 100644 ui/drivers/qt/gridview.cpp create mode 100644 ui/drivers/qt/gridview.h diff --git a/Makefile.common b/Makefile.common index ae3d10b3e4..5bf02a93ab 100644 --- a/Makefile.common +++ b/Makefile.common @@ -342,7 +342,7 @@ ifeq ($(HAVE_QT), 1) ui/drivers/qt/ui_qt_browser_window.o \ ui/drivers/qt/ui_qt_load_core_window.o \ ui/drivers/qt/ui_qt_msg_window.o \ - ui/drivers/qt/flowlayout.o \ + ui/drivers/qt/gridview.o \ ui/drivers/qt/shaderparamsdialog.o \ ui/drivers/qt/coreoptionsdialog.o \ ui/drivers/qt/filedropwidget.o \ @@ -357,7 +357,7 @@ ifeq ($(HAVE_QT), 1) MOC_HEADERS += ui/drivers/ui_qt.h \ ui/drivers/qt/ui_qt_load_core_window.h \ - ui/drivers/qt/flowlayout.h \ + ui/drivers/qt/gridview.h \ ui/drivers/qt/shaderparamsdialog.h \ ui/drivers/qt/coreoptionsdialog.h \ ui/drivers/qt/filedropwidget.h \ diff --git a/griffin/griffin_cpp.cpp b/griffin/griffin_cpp.cpp index fd3673afa0..e34561659f 100644 --- a/griffin/griffin_cpp.cpp +++ b/griffin/griffin_cpp.cpp @@ -43,7 +43,7 @@ UI #include "../ui/drivers/qt/ui_qt_browser_window.cpp" #include "../ui/drivers/qt/ui_qt_msg_window.cpp" #include "../ui/drivers/qt/ui_qt_application.cpp" -#include "../ui/drivers/qt/flowlayout.cpp" +#include "../ui/drivers/qt/gridview.cpp" #include "../ui/drivers/qt/shaderparamsdialog.cpp" #include "../ui/drivers/qt/coreoptionsdialog.cpp" #include "../ui/drivers/qt/filedropwidget.cpp" @@ -59,7 +59,7 @@ UI #include "../ui/drivers/qt/moc_coreinfodialog.cpp" #include "../ui/drivers/qt/moc_coreoptionsdialog.cpp" #include "../ui/drivers/qt/moc_filedropwidget.cpp" -#include "../ui/drivers/qt/moc_flowlayout.cpp" +#include "../ui/drivers/qt/moc_gridview.cpp" #include "../ui/drivers/qt/moc_playlistentrydialog.cpp" #include "../ui/drivers/qt/moc_shaderparamsdialog.cpp" #include "../ui/drivers/qt/moc_ui_qt_load_core_window.cpp" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 9a4ae85a39..bf9b3fe806 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -7544,6 +7544,14 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST, "Start on playlist:" ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE, + "Icon view thumbnail type:" + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT, + "Thumbnail cache limit:" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_QT_DOWNLOAD_ALL_THUMBNAILS, "Download All Thumbnails" @@ -7779,4 +7787,4 @@ MSG_HASH( MSG_HASH( MSG_MISSING_ASSETS, "Warning: Missing assets, use the Online Updater if available" - ) \ No newline at end of file + ) diff --git a/msg_hash.h b/msg_hash.h index 08fba9705c..93cac5f1ce 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1967,6 +1967,8 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT, MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT, MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE, + MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT, MENU_ENUM_LABEL_VALUE_QT_MENU_TOOLS, MENU_ENUM_LABEL_VALUE_QT_MENU_HELP, MENU_ENUM_LABEL_VALUE_QT_MENU_DOCK_CONTENT_BROWSER, diff --git a/ui/drivers/qt/filedropwidget.cpp b/ui/drivers/qt/filedropwidget.cpp index a53b588e7e..8a336288b2 100644 --- a/ui/drivers/qt/filedropwidget.cpp +++ b/ui/drivers/qt/filedropwidget.cpp @@ -41,7 +41,12 @@ void FileDropWidget::paintEvent(QPaintEvent *event) void FileDropWidget::keyPressEvent(QKeyEvent *event) { - if (event->key() == Qt::Key_Delete) + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + { + event->accept(); + emit enterPressed(); + } + else if (event->key() == Qt::Key_Delete) { event->accept(); emit deletePressed(); diff --git a/ui/drivers/qt/filedropwidget.h b/ui/drivers/qt/filedropwidget.h index 9301d948df..3a5d420e5a 100644 --- a/ui/drivers/qt/filedropwidget.h +++ b/ui/drivers/qt/filedropwidget.h @@ -15,6 +15,7 @@ public: FileDropWidget(QWidget *parent = 0); signals: void filesDropped(QStringList files); + void enterPressed(); void deletePressed(); protected: void dragEnterEvent(QDragEnterEvent *event); diff --git a/ui/drivers/qt/flowlayout.cpp b/ui/drivers/qt/flowlayout.cpp deleted file mode 100644 index b970e10b10..0000000000 --- a/ui/drivers/qt/flowlayout.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* Original work Copyright (C) 2016 The Qt Company Ltd. - * Modified work Copyright (C) 2018 - Brad Parker - */ - -#include - -#include "flowlayout.h" -#include "../ui_qt.h" - -FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) - : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); - - connect(this, SIGNAL(signalAddWidgetDeferred(QPointer)), this, SLOT(onAddWidgetDeferred(QPointer)), Qt::QueuedConnection); -} - -FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) - : m_hSpace(hSpacing), m_vSpace(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} - -FlowLayout::~FlowLayout() -{ - QLayoutItem *item = NULL; - - while ((item = takeAt(0)) != NULL) - delete item; -} - -void FlowLayout::addItem(QLayoutItem *item) -{ - itemList.append(item); -} - -int FlowLayout::horizontalSpacing() const -{ - if (m_hSpace >= 0) - return m_hSpace; - else - return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); -} - -int FlowLayout::verticalSpacing() const -{ - if (m_vSpace >= 0) - return m_vSpace; - else - return smartSpacing(QStyle::PM_LayoutVerticalSpacing); -} - -int FlowLayout::count() const -{ - return itemList.size(); -} - -QLayoutItem* FlowLayout::itemAt(int index) const -{ - return itemList.value(index); -} - -QLayoutItem* FlowLayout::takeAt(int index) -{ - if (index >= 0 && index < itemList.size()) - return itemList.takeAt(index); - else - return NULL; -} - -Qt::Orientations FlowLayout::expandingDirections() const -{ - return 0; -} - -bool FlowLayout::hasHeightForWidth() const -{ - return true; -} - -int FlowLayout::heightForWidth(int width) const -{ - int height = doLayout(QRect(0, 0, width, 0), true); - return height; -} - -void FlowLayout::setGeometry(const QRect &rect) -{ - QLayout::setGeometry(rect); - doLayout(rect, false); -} - -QSize FlowLayout::sizeHint() const -{ - return minimumSize(); -} - -QSize FlowLayout::minimumSize() const -{ - QSize size; - int i = 0; - - if (itemList.isEmpty()) - return size; - - for (i = 0; i < itemList.count(); i++) - { - const QLayoutItem *item = itemList.at(i); - size = size.expandedTo(item->minimumSize()); - } - - size += QSize(2 * margin(), 2 * margin()); - return size; -} - -int FlowLayout::doLayout(const QRect &rect, bool testOnly) const -{ - QRect effectiveRect; - int left = 0, top = 0, right = 0, bottom = 0; - int x = 0; - int y = 0; - int lineHeight = 0; - int i = 0; - - getContentsMargins(&left, &top, &right, &bottom); - effectiveRect = rect.adjusted(+left, +top, -right, -bottom); - x = effectiveRect.x(); - y = effectiveRect.y(); - - if (itemList.isEmpty()) - return y + lineHeight - rect.y() + bottom; - - for (i = 0; i < itemList.count(); i++) - { - QLayoutItem *item = itemList.at(i); - const QWidget *wid = item->widget(); - int spaceX = horizontalSpacing(); - int spaceY = 0; - int nextX = 0; - - if (spaceX == -1) - spaceX = wid->style()->layoutSpacing( - QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - - spaceY = verticalSpacing(); - - if (spaceY == -1) - spaceY = wid->style()->layoutSpacing( - QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - - nextX = x + item->sizeHint().width() + spaceX; - - if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) - { - x = effectiveRect.x(); - y = y + lineHeight + spaceY; - nextX = x + item->sizeHint().width() + spaceX; - lineHeight = 0; - } - - if (!testOnly) - item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); - - x = nextX; - lineHeight = qMax(lineHeight, item->sizeHint().height()); - } - - return y + lineHeight - rect.y() + bottom; -} - -int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const -{ - const QObject *parentObj = parent(); - - if (!parentObj) - return -1; - else if (parentObj->isWidgetType()) - { - const QWidget *pw = static_cast(parentObj); - return pw->style()->pixelMetric(pm, NULL, pw); - } - else - return static_cast(parentObj)->spacing(); -} - -void FlowLayout::addWidgetDeferred(QPointer widget) -{ - emit signalAddWidgetDeferred(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 deleted file mode 100644 index c9d11e7653..0000000000 --- a/ui/drivers/qt/flowlayout.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/* Original work Copyright (C) 2016 The Qt Company Ltd. - * Modified work Copyright (C) 2018 - Brad Parker - */ - -/* bparker: Removed C++11 override keyword from original source - * Changed QList to QVector - */ - -#ifndef FLOWLAYOUT_H -#define FLOWLAYOUT_H - -#include -#include -#include - -class ThumbnailWidget; - -class FlowLayout : public QLayout -{ - Q_OBJECT -public: - explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); - explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); - ~FlowLayout(); - - void addItem(QLayoutItem *item); - int horizontalSpacing() const; - int verticalSpacing() const; - Qt::Orientations expandingDirections() const; - bool hasHeightForWidth() const; - int heightForWidth(int) const; - int count() const; - QLayoutItem* itemAt(int index) const; - QSize minimumSize() const; - void setGeometry(const QRect &rect); - QSize sizeHint() const; - QLayoutItem* takeAt(int index); - void addWidgetDeferred(QPointer widget); - -signals: - void signalAddWidgetDeferred(QPointer widget); - -private slots: - void onAddWidgetDeferred(QPointer widget); - -private: - int doLayout(const QRect &rect, bool testOnly) const; - int smartSpacing(QStyle::PixelMetric pm) const; - - QVector itemList; - int m_hSpace; - int m_vSpace; -}; - -#endif // FLOWLAYOUT_H diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp new file mode 100644 index 0000000000..5ba83ba1a8 --- /dev/null +++ b/ui/drivers/qt/gridview.cpp @@ -0,0 +1,392 @@ +#include +#include + +#include "gridview.h" +#include "../ui_qt.h" + +/* http://www.informit.com/articles/article.aspx?p=1613548 */ + +ThumbnailDelegate::ThumbnailDelegate(QObject* parent) : + QStyledItemDelegate(parent) +{ +} + +void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const +{ + painter->save(); + + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + + const QWidget *widget = opt.widget; + + QStyle *style = widget->style(); + + int margin = 11; + int textMargin = 4; + QRect rect = opt.rect; + int textHeight = painter->fontMetrics().height() + margin + margin; + QRect adjusted = rect.adjusted(margin, margin, -margin, -textHeight + textMargin); + QPixmap pixmap = index.data(PlaylistModel::THUMBNAIL).value(); + + // draw the background + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); + + // draw the image + if (!pixmap.isNull()) + { + QPixmap pixmapScaled = pixmap.scaled(adjusted.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); + style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | Qt::AlignBottom, pixmapScaled); + } + + // draw the text + if (!opt.text.isEmpty()) + { + QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + + if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) + cg = QPalette::Inactive; + + if (opt.state & QStyle::State_Selected) + painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); + else + painter->setPen(opt.palette.color(cg, QPalette::Text)); + + QRect textRect = QRect(rect.x() + margin, rect.y() + adjusted.height() - textMargin + margin, rect.width() - 2 * margin, textHeight); + QString elidedText = painter->fontMetrics().elidedText(opt.text, opt.textElideMode, textRect.width(), Qt::TextShowMnemonic); + + painter->setFont(opt.font); + painter->drawText(textRect, Qt::AlignCenter, elidedText); + } + + painter->restore(); +} + +GridView::GridView(QWidget *parent) : QAbstractItemView(parent), m_idealHeight(0), m_hashIsDirty(false) +{ + setFocusPolicy(Qt::WheelFocus); + horizontalScrollBar()->setRange(0, 0); + verticalScrollBar()->setRange(0, 0); +} + +void GridView::setModel(QAbstractItemModel *newModel) +{ + if (model()) + disconnect(model(), SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(rowsRemoved(QModelIndex, int, int))); + + QAbstractItemView::setModel(newModel); + + connect(newModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(rowsRemoved(QModelIndex, int, int))); + + m_hashIsDirty = true; +} + +void GridView::setviewMode(ViewMode mode) +{ + m_viewMode = mode; +} + +void GridView::calculateRectsIfNecessary() const +{ + if (!m_hashIsDirty) + return; + + int x = m_spacing; + int y = m_spacing; + int row; + int nextX; + + const int maxWidth = viewport()->width(); + + switch (m_viewMode) { + case Anchored: + { + int columns = (maxWidth - m_spacing) / (m_size + m_spacing); + if (columns > 0) + { + const int actualSpacing = (maxWidth - m_spacing - m_size - (columns - 1) * m_size) / columns; + for (row = 0; row < model()->rowCount(); ++row) + { + nextX = x + m_size + actualSpacing; + if (nextX > maxWidth) + { + x = m_spacing; + y += m_size + m_spacing; + nextX = x + m_size + actualSpacing; + } + m_rectForRow[row] = QRectF(x, y, m_size, m_size); + x = nextX; + } + m_idealHeight = y + m_size + m_spacing; + } + break; + } + case Centered: + { + int columns = (maxWidth - m_spacing) / (m_size + m_spacing); + if (columns > 0) + { + const int actualSpacing = (maxWidth - columns * m_size) / (columns + 1); + x = actualSpacing; + for (row = 0; row < model()->rowCount(); ++row) + { + nextX = x + m_size + actualSpacing; + if (nextX > maxWidth) + { + x = actualSpacing; + y += m_size + m_spacing; + nextX = x + m_size + actualSpacing; + } + m_rectForRow[row] = QRectF(x, y, m_size, m_size); + x = nextX; + } + m_idealHeight = y + m_size + m_spacing; + } + break; + } + case Simple: + for (row = 0; row < model()->rowCount(); ++row) + { + nextX = x + m_size + m_spacing; + if (nextX > maxWidth) + { + x = m_spacing; + y += m_size + m_spacing; + nextX = x + m_size + m_spacing; + } + m_rectForRow[row] = QRectF(x, y, m_size, m_size); + x = nextX; + } + break; + } + + m_hashIsDirty = false; + viewport()->update(); +} + +QRect GridView::visualRect(const QModelIndex &index) const +{ + QRect rect; + if (index.isValid()) + rect = viewportRectForRow(index.row()).toRect(); + return rect; +} + +QRectF GridView::viewportRectForRow(int row) const +{ + calculateRectsIfNecessary(); + QRectF rect = m_rectForRow.value(row).toRect(); + if (!rect.isValid()) + return rect; + return QRectF(rect.x() - horizontalScrollBar()->value(), rect.y() - verticalScrollBar()->value(), rect.width(), rect.height()); +} + +void GridView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint) +{ + QRect viewRect = viewport()->rect(); + QRect itemRect = visualRect(index); + + if (itemRect.left() < viewRect.left()) + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + itemRect.left() - viewRect.left()); + else if (itemRect.right() > viewRect.right()) + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + qMin(itemRect.right() - viewRect.right(), itemRect.left() - viewRect.left())); + if (itemRect.top() < viewRect.top()) + verticalScrollBar()->setValue(verticalScrollBar()->value() + itemRect.top() - viewRect.top()); + else if (itemRect.bottom() > viewRect.bottom()) + verticalScrollBar()->setValue(verticalScrollBar()->value() + qMin(itemRect.bottom() - viewRect.bottom(), itemRect.top() - viewRect.top())); + viewport()->update(); +} + +QModelIndex GridView::indexAt(const QPoint &point_) const +{ + QPoint point(point_); + point.rx() += horizontalScrollBar()->value(); + point.ry() += verticalScrollBar()->value(); + calculateRectsIfNecessary(); + QHashIterator i(m_rectForRow); + while (i.hasNext()) + { + i.next(); + if (i.value().contains(point)) + return model()->index(i.key(), 0, rootIndex()); + } + return QModelIndex(); +} + +void GridView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + m_hashIsDirty = true; + QAbstractItemView::dataChanged(topLeft, bottomRight); +} + +void GridView::refresh() +{ + m_hashIsDirty = true; + calculateRectsIfNecessary(); + updateGeometries(); +} + +void GridView::rowsInserted(const QModelIndex &parent, int start, int end) +{ + QAbstractItemView::rowsInserted(parent, start, end); + refresh(); +} + +void GridView::rowsRemoved(const QModelIndex &parent, int start, int end) +{ + refresh(); +} + +void GridView::setGridSize(const int newSize) +{ + if (newSize != m_size) + { + m_size = newSize; + refresh(); + } +} + +void GridView::resizeEvent(QResizeEvent*) +{ + refresh(); +} + +void GridView::reset() +{ + QAbstractItemView::reset(); + refresh(); +} + +QModelIndex GridView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers) +{ + QModelIndex index = currentIndex(); + if (index.isValid()) + { + if ((cursorAction == MoveLeft && index.row() > 0) || (cursorAction == MoveRight && index.row() + 1 < model()->rowCount())) + { + const int offset = (cursorAction == MoveLeft ? -1 : 1); + index = model()->index(index.row() + offset, index.column(), index.parent()); + } + else if ((cursorAction == MoveUp && index.row() > 0) || (cursorAction == MoveDown && index.row() + 1 < model()->rowCount())) + { + const int offset = ((m_size + m_spacing) * (cursorAction == MoveUp ? -1 : 1)); + QRect rect = viewportRectForRow(index.row()).toRect(); + QPoint point(rect.center().x(), rect.center().y() + offset); + index = indexAt(point); + } + } + return index; +} + +int GridView::horizontalOffset() const +{ + return horizontalScrollBar()->value(); +} + +int GridView::verticalOffset() const +{ + return verticalScrollBar()->value(); +} + +void GridView::scrollContentsBy(int dx, int dy) +{ + scrollDirtyRegion(dx, dy); + viewport()->scroll(dx, dy); + emit(visibleItemsChangedMaybe()); +} + +QVector GridView::visibleIndexes() const { + return m_visibleIndexes; +} + +void GridView::setSelection(const QRect &rect, QFlags flags) +{ + QRect rectangle = rect.translated(horizontalScrollBar()->value(), verticalScrollBar()->value()).normalized(); + calculateRectsIfNecessary(); + QHashIterator i(m_rectForRow); + int firstRow = model()->rowCount(); + int lastRow = -1; + while (i.hasNext()) + { + i.next(); + if (i.value().intersects(rectangle)) + { + firstRow = firstRow < i.key() ? firstRow : i.key(); + lastRow = lastRow > i.key() ? lastRow : i.key(); + } + } + if (firstRow != model()->rowCount() && lastRow != -1) + { + QItemSelection selection( model()->index(firstRow, 0, rootIndex()), model()->index(lastRow, 0, rootIndex())); + selectionModel()->select(selection, flags); + } + else + { + QModelIndex invalid; + QItemSelection selection(invalid, invalid); + selectionModel()->select(selection, flags); + } +} + +QRegion GridView::visualRegionForSelection(const QItemSelection &selection) const +{ + QRegion region; + foreach(const QItemSelectionRange &range, selection) + { + for (int row = range.top(); row <= range.bottom(); ++row) + { + for (int column = range.left(); column < range.right(); ++column) + { + QModelIndex index = model()->index(row, column, rootIndex()); + region += visualRect(index); + } + } + } + return region; +} + +void GridView::paintEvent(QPaintEvent*) +{ + QPainter painter(viewport()); + int row; + + painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + m_visibleIndexes.clear(); + + for (row = 0; row < model()->rowCount(rootIndex()); ++row) + { + QModelIndex index = model()->index(row, 0, rootIndex()); + QRectF rect = viewportRectForRow(row); + + if (!rect.isValid() || rect.bottom() < 0 || rect.y() > viewport()->height()) + continue; + + m_visibleIndexes.append(index); + QStyleOptionViewItem option = viewOptions(); + option.rect = rect.toRect(); + + if (selectionModel()->isSelected(index)) + option.state |= QStyle::State_Selected; + + if (currentIndex() == index) + option.state |= QStyle::State_HasFocus; + + itemDelegate()->paint(&painter, option, index); + } +} + +void GridView::updateGeometries() +{ + const int RowHeight = m_size + m_spacing; + + QAbstractItemView::updateGeometries(); + + verticalScrollBar()->setSingleStep(RowHeight); + verticalScrollBar()->setPageStep(viewport()->height()); + verticalScrollBar()->setRange(0, qMax(0, m_idealHeight - viewport()->height())); + + horizontalScrollBar()->setPageStep(viewport()->width()); + horizontalScrollBar()->setRange(0, qMax(0, RowHeight - viewport()->width())); + + emit(visibleItemsChangedMaybe()); +} diff --git a/ui/drivers/qt/gridview.h b/ui/drivers/qt/gridview.h new file mode 100644 index 0000000000..06537ec254 --- /dev/null +++ b/ui/drivers/qt/gridview.h @@ -0,0 +1,74 @@ +#ifndef GRIDVIEW_H +#define GRIDVIEW_H + +#include +#include + +class ThumbnailDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + ThumbnailDelegate(QObject* parent = 0); + void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const; +}; + +class GridView : public QAbstractItemView +{ + Q_OBJECT + +public: + enum ViewMode + { + Simple, + Centered, + Anchored + }; + + GridView(QWidget *parent = 0); + ~GridView() {} + + QModelIndex indexAt(const QPoint &point_) const; + QVector visibleIndexes() const; + QRect visualRect(const QModelIndex &index) const; + void setModel(QAbstractItemModel *model); + void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint); + void setGridSize(const int newSize); + void setviewMode(ViewMode mode); + +signals: + void visibleItemsChangedMaybe() const; + +protected slots: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void rowsInserted(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex &parent, int start, int end); + void updateGeometries(); + void reset() override; + +protected: + QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers); + QRegion visualRegionForSelection(const QItemSelection &selection) const; + bool isIndexHidden(const QModelIndex&) const { return false; } + int horizontalOffset() const; + int verticalOffset() const; + void scrollContentsBy(int dx, int dy); + void setSelection(const QRect &rect, QFlags flags); + void paintEvent(QPaintEvent*); + void resizeEvent(QResizeEvent*); + +private: + QRectF viewportRectForRow(int row) const; + void calculateRectsIfNecessary() const; + void refresh(); + + int m_size = 255; + int m_spacing = 7; + QVector m_visibleIndexes; + ViewMode m_viewMode = Centered; + mutable int m_idealHeight; + mutable QHash m_rectForRow; + mutable bool m_hashIsDirty; +}; + +#endif // GRIDVIEW_H diff --git a/ui/drivers/qt/playlist.cpp b/ui/drivers/qt/playlist.cpp index 7217f19f5a..7021c85d47 100644 --- a/ui/drivers/qt/playlist.cpp +++ b/ui/drivers/qt/playlist.cpp @@ -9,9 +9,10 @@ #include #include #include +#include +#include #include "../ui_qt.h" -#include "flowlayout.h" #include "playlistentrydialog.h" extern "C" { @@ -28,6 +29,213 @@ extern "C" { #include "../../../verbosity.h" } +PlaylistModel::PlaylistModel(QObject *parent) + : QAbstractListModel(parent) +{ + m_imageFormats = QVector::fromList(QImageReader::supportedImageFormats()); + m_fileSanitizerRegex = QRegularExpression("[&*/:`<>?\\|]"); + setThumbnailCacheLimit(500); + connect(this, &PlaylistModel::imageLoaded, this, &PlaylistModel::onImageLoaded); +} + +int PlaylistModel::rowCount(const QModelIndex & /* parent */) const +{ + return m_contents.count(); +} + +int PlaylistModel::columnCount(const QModelIndex & /* parent */) const +{ + return 1; +} + +QVariant PlaylistModel::data(const QModelIndex &index, int role) const +{ + if (index.column() == 0) + { + if (!index.isValid()) + return QVariant(); + + if (index.row() >= m_contents.size() || index.row() < 0) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + case Qt::ToolTipRole: + return m_contents.at(index.row())["label_noext"]; + case HASH: + return QVariant::fromValue(m_contents.at(index.row())); + case THUMBNAIL: + { + QPixmap *cachedPreview = m_cache.object(getCurrentTypeThumbnailPath(index)); + if (cachedPreview) + return *cachedPreview; + return QVariant(); + } + } + } + return QVariant(); +} + +Qt::ItemFlags PlaylistModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractListModel::flags(index) | Qt::ItemIsEditable; +} + +bool PlaylistModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) { + QHash hash = m_contents.at(index.row()); + + hash["label"] = value.toString(); + hash["label_noext"] = QFileInfo(value.toString()).completeBaseName(); + + m_contents.replace(index.row(), hash); + emit dataChanged(index, index, { role }); + return true; + } + return false; +} + +QVariant PlaylistModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + return msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); + else + return section + 1; +} + +void PlaylistModel::setThumbnailType(const ThumbnailType type) +{ + m_thumbnailType = type; +} + +void PlaylistModel::setThumbnailCacheLimit(int limit) +{ + m_cache.setMaxCost(limit * 1024); +} + +QString PlaylistModel::getThumbnailPath(const QModelIndex &index, QString type) const +{ + QByteArray extension; + QString extensionStr; + + QString thumbnailFileNameNoExt; + int lastIndex = -1; + + const QHash &hash = m_contents.at(index.row()); + lastIndex = hash["path"].lastIndexOf('.'); + + if (lastIndex >= 0) + { + extensionStr = hash["path"].mid(lastIndex + 1); + + if (!extensionStr.isEmpty()) + { + extension = extensionStr.toLower().toUtf8(); + } + } + + if (!extension.isEmpty() && m_imageFormats.contains(extension)) + { + /* use thumbnail widgets to show regular image files */ + return hash["path"]; + } + else + { + thumbnailFileNameNoExt = hash["label_noext"]; + thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); + return QDir::cleanPath(QString(config_get_ptr()->paths.directory_thumbnails)) + "/" + hash.value("db_name") + "/" + type + "/" + thumbnailFileNameNoExt + ".png"; + } +} + +QString PlaylistModel::getCurrentTypeThumbnailPath(const QModelIndex &index) const +{ + switch (m_thumbnailType) + { + case THUMBNAIL_TYPE_BOXART: + return getThumbnailPath(index, THUMBNAIL_BOXART); + case THUMBNAIL_TYPE_SCREENSHOT: + return getThumbnailPath(index, THUMBNAIL_SCREENSHOT); + case THUMBNAIL_TYPE_TITLE_SCREEN: + return getThumbnailPath(index, THUMBNAIL_TITLE); + default: + return QString(); + } +} + +void PlaylistModel::reloadThumbnail(const QModelIndex &index) +{ + if (index.isValid()) { + reloadThumbnailPath(getCurrentTypeThumbnailPath(index)); + loadThumbnail(index); + } +} + +void PlaylistModel::reloadSystemThumbnails(const QString system) +{ + int i = 0; + QString key; + QString path = QDir::cleanPath(QString(config_get_ptr()->paths.directory_thumbnails)) + "/" + system; + QList keys = m_cache.keys(); + QList pending = m_pendingImages.values(); + + for (i = 0; i < keys.size(); i++) + { + key = keys.at(i); + if (key.startsWith(path)) + m_cache.remove(key); + } + + for (i = 0; i < pending.size(); i++) + { + key = pending.at(i); + if (key.startsWith(path)) + m_pendingImages.remove(key); + } +} + +void PlaylistModel::reloadThumbnailPath(const QString path) +{ + m_cache.remove(path); + m_pendingImages.remove(path); +} + +void PlaylistModel::loadThumbnail(const QModelIndex &index) +{ + QString path = getCurrentTypeThumbnailPath(index); + + if (!m_pendingImages.contains(path) && !m_cache.contains(path)) + { + m_pendingImages.insert(path); + QtConcurrent::run(this, &PlaylistModel::loadImage, index, path); + } +} + +void PlaylistModel::loadImage(const QModelIndex &index, const QString &path) +{ + const QImage image = QImage(path); + if (!image.isNull()) + emit imageLoaded(image, index, path); +} + +void PlaylistModel::onImageLoaded(const QImage image, const QModelIndex &index, const QString &path) +{ + QPixmap *pixmap = new QPixmap(QPixmap::fromImage(image)); + const int cost = pixmap->width() * pixmap->height() * pixmap->depth() / (8 * 1024); + m_cache.insert(path, pixmap, cost); + if (index.isValid()) + emit dataChanged(index, index, { THUMBNAIL }); + m_pendingImages.remove(path); +} + inline static bool comp_hash_name_key_lower(const QHash &lhs, const QHash &rhs) { return lhs.value("name").toLower() < rhs.value("name").toLower(); @@ -38,7 +246,6 @@ inline static bool comp_hash_label_key_lower(const QHash &lhs, return lhs.value("label").toLower() < rhs.value("label").toLower(); } -/* https://stackoverflow.com/questions/7246622/how-to-create-a-slider-with-a-non-linear-scale */ bool MainWindow::addDirectoryFilesToList(QProgressDialog *dialog, QStringList &list, QDir &dir, QStringList &extensions) { PlaylistEntryDialog *playlistDialog = playlistEntryDialog(); @@ -834,9 +1041,6 @@ void MainWindow::reloadPlaylists() getPlaylistFiles(); - /* block this signal because setData() would trigger an infinite loop */ - disconnect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); - m_listWidget->clear(); m_listWidget->setSelectionBehavior(QAbstractItemView::SelectRows); m_listWidget->setSelectionMode(QAbstractItemView::SingleSelection); @@ -968,7 +1172,6 @@ void MainWindow::reloadPlaylists() } } - connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); } QString MainWindow::getCurrentPlaylistPath() @@ -1116,151 +1319,15 @@ void MainWindow::getPlaylistFiles() m_playlistFiles = playlistDir.entryList(QDir::NoDotAndDotDot | QDir::Readable | QDir::Files, QDir::Name); } -void MainWindow::addPlaylistItemsToGrid(const QStringList &paths, bool add) -{ - QVector > items; - int i; - - if (paths.isEmpty()) - return; - - for (i = 0; i < paths.size(); i++) - { - int j; - QVector > vec = getPlaylistItems(paths.at(i)); - /* QVector::append() wasn't added until 5.5, so just do it the old fashioned way */ - for (j = 0; j < vec.size(); j++) - { - if (add && m_allPlaylistsGridMaxCount > 0 && items.size() >= m_allPlaylistsGridMaxCount) - goto finish; - - items.append(vec.at(j)); - } - } -finish: - std::sort(items.begin(), items.end(), comp_hash_label_key_lower); - - addPlaylistHashToGrid(items); -} - -void MainWindow::addPlaylistHashToGrid(const QVector > &items) -{ - QScreen *screen = qApp->primaryScreen(); - QSize screenSize = screen->size(); - QListWidgetItem *currentItem = m_listWidget->currentItem(); - settings_t *settings = config_get_ptr(); - int i = 0; - int zoomValue = m_zoomSlider->value(); - - m_gridProgressBar->setMinimum(0); - m_gridProgressBar->setMaximum(qMax(0, items.count() - 1)); - m_gridProgressBar->setValue(0); - - for (i = 0; i < items.count(); i++) - { - const QHash &hash = items.at(i); - QPointer item; - QPointer label; - QString thumbnailFileNameNoExt; - QLabel *newLabel = NULL; - QSize thumbnailWidgetSizeHint(screenSize.width() / 8, screenSize.height() / 8); - QByteArray extension; - QString extensionStr; - QString imagePath; - int lastIndex = -1; - - if (m_listWidget->currentItem() != currentItem) - { - /* user changed the current playlist before we finished loading... abort */ - m_gridProgressWidget->hide(); - break; - } - - item = new GridItem(); - - lastIndex = hash["path"].lastIndexOf('.'); - - if (lastIndex >= 0) - { - extensionStr = hash["path"].mid(lastIndex + 1); - - if (!extensionStr.isEmpty()) - { - extension = extensionStr.toLower().toUtf8(); - } - } - - if (!extension.isEmpty() && m_imageFormats.contains(extension)) - { - /* use thumbnail widgets to show regular image files */ - imagePath = hash["path"]; - } - else - { - thumbnailFileNameNoExt = hash["label_noext"]; - thumbnailFileNameNoExt.replace(m_fileSanitizerRegex, "_"); - imagePath = QString(settings->paths.directory_thumbnails) + "/" + hash.value("db_name") + "/" + THUMBNAIL_BOXART + "/" + thumbnailFileNameNoExt + ".png"; - } - - item->hash = hash; - item->widget = new ThumbnailWidget(); - item->widget->setSizeHint(thumbnailWidgetSizeHint); - item->widget->setFixedSize(item->widget->sizeHint()); - item->widget->setLayout(new QVBoxLayout()); - item->widget->setObjectName("thumbnailWidget"); - item->widget->setProperty("hash", QVariant::fromValue >(hash)); - item->widget->setProperty("image_path", imagePath); - - connect(item->widget, SIGNAL(mouseDoubleClicked()), this, SLOT(onGridItemDoubleClicked())); - connect(item->widget, SIGNAL(mousePressed()), this, SLOT(onGridItemClicked())); - - label = new ThumbnailLabel(item->widget); - label->setObjectName("thumbnailGridLabel"); - - item->label = label; - item->labelText = hash.value("label"); - - newLabel = new QLabel(item->labelText, item->widget); - newLabel->setObjectName("thumbnailQLabel"); - newLabel->setAlignment(Qt::AlignCenter); - newLabel->setToolTip(item->labelText); - - calcGridItemSize(item, zoomValue); - - item->widget->layout()->addWidget(label); - - item->widget->layout()->addWidget(newLabel); - qobject_cast(item->widget->layout())->setStretchFactor(label, 1); - - m_gridLayout->addWidgetDeferred(item->widget); - m_gridItems.append(item); - - loadImageDeferred(item, imagePath); - - if (i % 25 == 0) - { - /* Needed to update progress dialog while doing a lot of stuff on the main thread. */ - qApp->processEvents(); - } - - m_gridProgressBar->setValue(i); - } - - /* If there's only one entry, a min/max/value of all zero would make an indeterminate progress bar that never ends... so just hide it when we are done. */ - if (m_gridProgressBar->value() == m_gridProgressBar->maximum()) - m_gridProgressWidget->hide(); -} - -QVector > MainWindow::getPlaylistItems(QString pathString) +void PlaylistModel::getPlaylistItems(QString path) { QByteArray pathArray; - QVector > items; const char *pathData = NULL; playlist_t *playlist = NULL; unsigned playlistSize = 0; unsigned i = 0; - pathArray.append(pathString); + pathArray.append(path); pathData = pathArray.constData(); playlist = playlist_init(pathData, COLLECTION_SIZE); @@ -1313,58 +1380,67 @@ QVector > MainWindow::getPlaylistItems(QString pathStrin hash["db_name"].remove(file_path_str(FILE_PATH_LPL_EXTENSION)); } - items.append(hash); + m_contents.append(hash); } playlist_free(playlist); playlist = NULL; - - return items; } -void MainWindow::addPlaylistItemsToTable(const QStringList &paths, bool add) +void PlaylistModel::addPlaylistItems(const QStringList &paths, bool add) { - QVector > items; int i; if (paths.isEmpty()) return; + beginResetModel(); + + m_contents.clear(); + for (i = 0; i < paths.size(); i++) { - int j; - QVector > vec = getPlaylistItems(paths.at(i)); - /* QVector::append() wasn't added until 5.5, so just do it the old fashioned way */ - for (j = 0; j < vec.size(); j++) - { - if (add && m_allPlaylistsListMaxCount > 0 && items.size() >= m_allPlaylistsListMaxCount) - goto finish; - - items.append(vec.at(j)); - } + getPlaylistItems(paths.at(i)); } -finish: - addPlaylistHashToTable(items); + + endResetModel(); } -void MainWindow::addPlaylistHashToTable(const QVector > &items) +void PlaylistModel::addDir(QString path, QFlags showHidden) { + QDir dir = path; + QStringList dirList; int i = 0; - int oldRowCount = m_tableWidget->rowCount(); - m_tableWidget->setRowCount(oldRowCount + items.count()); + dirList = dir.entryList(QDir::NoDotAndDotDot | + QDir::Readable | + QDir::Files | + showHidden, + QDir::Name); - for (i = 0; i < items.count(); i++) + if (dirList.count() == 0) + return; + + beginResetModel(); + + m_contents.clear(); + + for (i = 0; i < dirList.count(); i++) { - QTableWidgetItem *labelItem = NULL; - const QHash &hash = items.at(i); + QString fileName = dirList.at(i); + QHash hash; + QString filePath(QDir::toNativeSeparators(dir.absoluteFilePath(fileName))); + QFileInfo fileInfo(filePath); - labelItem = new QTableWidgetItem(hash.value("label")); - labelItem->setData(Qt::UserRole, QVariant::fromValue >(hash)); - labelItem->setFlags(labelItem->flags() | Qt::ItemIsEditable); + hash["path"] = filePath; + hash["label"] = hash["path"]; + hash["label_noext"] = fileInfo.completeBaseName(); + hash["db_name"] = fileInfo.dir().dirName(); - m_tableWidget->setItem(oldRowCount + i, 0, labelItem); + m_contents.append(hash); } + + endResetModel(); } void MainWindow::setAllPlaylistsListMaxCount(int count) @@ -1382,4 +1458,3 @@ void MainWindow::setAllPlaylistsGridMaxCount(int count) m_allPlaylistsGridMaxCount = count; } - diff --git a/ui/drivers/qt/playlistthumbnaildownload.cpp b/ui/drivers/qt/playlistthumbnaildownload.cpp index e14558ea9d..3dab4cd02f 100644 --- a/ui/drivers/qt/playlistthumbnaildownload.cpp +++ b/ui/drivers/qt/playlistthumbnaildownload.cpp @@ -150,14 +150,14 @@ void MainWindow::onPlaylistThumbnailDownloadFinished() emit showErrorMessageDeferred(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NETWORK_ERROR)) + ": Code " + QString::number(code) + ": " + errorData);*/ } + m_playlistModel->reloadThumbnailPath(m_playlistThumbnailDownloadFile.fileName()); + if (!m_playlistThumbnailDownloadWasCanceled && m_pendingPlaylistThumbnails.count() > 0) { QHash nextThumbnail = m_pendingPlaylistThumbnails.takeAt(0); ViewType viewType = getCurrentViewType(); - if (viewType == VIEW_TYPE_ICONS) - emit gridItemChanged(reply->property("title").toString()); - + updateVisibleItems(); downloadNextPlaylistThumbnail(nextThumbnail.value("db_name"), nextThumbnail.value("label_noext"), nextThumbnail.value("type")); } else @@ -236,7 +236,7 @@ void MainWindow::downloadNextPlaylistThumbnail(QString system, QString title, QS if (!m_playlistThumbnailDownloadFile.open(QIODevice::WriteOnly)) { m_failedThumbnails++; - + RARCH_ERR("[Qt]: Could not open file for writing: %s\n", fileNameData); if (m_pendingPlaylistThumbnails.count() > 0) @@ -290,9 +290,9 @@ void MainWindow::downloadPlaylistThumbnails(QString playlistPath) QString system; QString title; QString type; - QVector > playlistItems = getPlaylistItems(playlistPath); settings_t *settings = config_get_ptr(); int i; + int count; if (!settings || !playlistFile.exists()) return; @@ -302,12 +302,14 @@ void MainWindow::downloadPlaylistThumbnails(QString playlistPath) m_failedThumbnails = 0; m_playlistThumbnailDownloadWasCanceled = false; - if (playlistItems.count() == 0) + count = m_playlistModel->rowCount(); + + if (count == 0) return; - for (i = 0; i < playlistItems.count(); i++) + for (i = 0; i < count; i++) { - const QHash &itemHash = playlistItems.at(i); + const QHash &itemHash = m_playlistModel->index(i, 0).data(PlaylistModel::HASH).value< QHash >(); QHash hash; QHash hash2; QHash hash3; diff --git a/ui/drivers/qt/thumbnaildownload.cpp b/ui/drivers/qt/thumbnaildownload.cpp index d83c8e186b..3e6a90ccb0 100644 --- a/ui/drivers/qt/thumbnaildownload.cpp +++ b/ui/drivers/qt/thumbnaildownload.cpp @@ -154,6 +154,8 @@ void MainWindow::onThumbnailDownloadFinished() { RARCH_LOG("[Qt]: Thumbnail download finished successfully.\n"); /* reload thumbnail image */ + m_playlistModel->reloadThumbnailPath(m_thumbnailDownloadFile.fileName()); + updateVisibleItems(); emit itemChanged(); } else diff --git a/ui/drivers/qt/thumbnailpackdownload.cpp b/ui/drivers/qt/thumbnailpackdownload.cpp index ac29d22fe0..48528ded06 100644 --- a/ui/drivers/qt/thumbnailpackdownload.cpp +++ b/ui/drivers/qt/thumbnailpackdownload.cpp @@ -191,7 +191,7 @@ void MainWindow::onThumbnailPackDownloadFinished() reply->disconnect(); reply->close(); - reply->deleteLater(); + //reply->deleteLater(); } void MainWindow::onThumbnailPackDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) @@ -309,6 +309,11 @@ void MainWindow::onThumbnailPackExtractFinished(bool success) emit showInfoMessageDeferred(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_PACK_DOWNLOADED_SUCCESSFULLY)); + QNetworkReply *reply = m_thumbnailPackDownloadReply.data(); + + m_playlistModel->reloadSystemThumbnails(reply->property("system").toString()); + reply->deleteLater(); + updateVisibleItems(); /* reload thumbnail image */ emit itemChanged(); } diff --git a/ui/drivers/qt/ui_qt_themes.h b/ui/drivers/qt/ui_qt_themes.h index 803cf8a37a..d4c3d327a9 100644 --- a/ui/drivers/qt/ui_qt_themes.h +++ b/ui/drivers/qt/ui_qt_themes.h @@ -313,7 +313,7 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral(R"( padding-left:5px; padding-right:5px; } - QTableWidget { + QTableView { background-color:rgb(25,25,25); alternate-background-color:rgb(40,40,40); } @@ -422,14 +422,14 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral(R"( QSizeGrip { background-color:solid; } - ThumbnailWidget#thumbnailWidget, ThumbnailLabel#thumbnailGridLabel, QLabel#thumbnailQLabel { + GridView::item { background-color:rgb(40,40,40); } - ThumbnailWidget#thumbnailWidgetSelected { - background-color:rgb(40,40,40); + GridView::item:selected { border:3px solid %1; } - QWidget#gridLayoutWidget { + GridView { background-color:rgb(25,25,25); + selection-color: white; } )"); diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 712137f68d..e7e05bf41b 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -41,7 +41,7 @@ #include "invader_png.h" #include "ui_qt_load_core_window.h" #include "ui_qt_themes.h" -#include "flowlayout.h" +#include "gridview.h" #include "shaderparamsdialog.h" #include "coreoptionsdialog.h" #include "filedropwidget.h" @@ -139,6 +139,7 @@ static void scan_finished_handler(void *task_data, void *user_data, const char * } #endif +/* 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; @@ -152,27 +153,6 @@ static double expScale(double inputValue, double midValue, double maxValue) return returnValue; } -/* https://gist.github.com/andrey-str/0f9c7709cbf0c9c49ef9 */ -static void setElidedText(QLabel *label, QWidget *clipWidget, int padding, const QString &text) -{ - QFontMetrics metrix(label->font()); - int width = clipWidget->width() - padding; - QString clippedText = metrix.elidedText(text, Qt::ElideRight, width); - label->setText(clippedText); -} - -GridItem::GridItem() : - QObject() - ,widget(NULL) - ,label(NULL) - ,hash() - ,image() - ,pixmap() - ,imageWatcher() - ,labelText() -{ -} - TreeView::TreeView(QWidget *parent) : QTreeView(parent) { @@ -192,26 +172,16 @@ void TreeView::selectionChanged(const QItemSelection &selected, const QItemSelec emit itemsSelected(list); } -TableWidget::TableWidget(QWidget *parent) : - QTableWidget(parent) +TableView::TableView(QWidget *parent) : + QTableView(parent) { } -bool TableWidget::isEditorOpen() +bool TableView::isEditorOpen() { return (state() == QAbstractItemView::EditingState); } -void TableWidget::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) - emit enterPressed(); - else if (event->key() == Qt::Key_Delete) - emit deletePressed(); - - QTableWidget::keyPressEvent(event); -} - ListWidget::ListWidget(QWidget *parent) : QListWidget(parent) { @@ -277,13 +247,14 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) ,m_loadCoreWindow(new LoadCoreWindow(this)) ,m_timer(new QTimer(this)) + ,m_thumbnailTimer(new QTimer(this)) ,m_currentCore() ,m_currentCoreVersion() ,m_statusLabel(new QLabel(this)) ,m_dirTree(new TreeView(this)) ,m_dirModel(new QFileSystemModel(m_dirTree)) ,m_listWidget(new ListWidget(this)) - ,m_tableWidget(new TableWidget(this)) + ,m_tableView(new TableView(this)) ,m_searchWidget(new QWidget(this)) ,m_searchLineEdit(new QLineEdit(this)) ,m_searchDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_EDIT_SEARCH), this)) @@ -311,18 +282,15 @@ MainWindow::MainWindow(QWidget *parent) : ,m_logDock(new QDockWidget(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_LOG), this)) ,m_logWidget(new QWidget(this)) ,m_logTextEdit(new LogTextEdit(m_logWidget)) - ,m_imageFormats() ,m_historyPlaylistsItem(NULL) ,m_folderIcon() ,m_customThemeString() - ,m_gridLayout(NULL) + ,m_gridView(new GridView(this)) ,m_gridWidget(new QWidget(this)) ,m_gridScrollArea(new QScrollArea(m_gridWidget)) - ,m_gridItems() ,m_gridLayoutWidget(new FileDropWidget()) ,m_zoomSlider(NULL) ,m_lastZoomSliderValue(0) - ,m_pendingItemUpdates() ,m_viewType(VIEW_TYPE_LIST) ,m_gridProgressBar(NULL) ,m_gridProgressWidget(NULL) @@ -370,6 +338,8 @@ MainWindow::MainWindow(QWidget *parent) : QLabel *gridProgressLabel = NULL; QHBoxLayout *gridFooterLayout = NULL; + //QApplication::setStyle(QStyleFactory::create("windowsvista")); + qRegisterMetaType >("ThumbnailWidget"); qRegisterMetaType("retro_task_callback_t"); @@ -409,15 +379,10 @@ MainWindow::MainWindow(QWidget *parent) : m_gridWidget->setLayout(new QVBoxLayout()); - m_gridLayout = new FlowLayout(m_gridLayoutWidget); - m_gridLayoutWidget->setObjectName("gridLayoutWidget"); + m_gridView->setSelectionMode(QAbstractItemView::SingleSelection); + m_gridView->setEditTriggers(QAbstractItemView::NoEditTriggers); - m_gridScrollArea->setAlignment(Qt::AlignCenter); - m_gridScrollArea->setFrameShape(QFrame::NoFrame); - m_gridScrollArea->setWidgetResizable(true); - m_gridScrollArea->setWidget(m_gridLayoutWidget); - - m_gridWidget->layout()->addWidget(m_gridScrollArea); + m_gridWidget->layout()->addWidget(m_gridView); m_gridWidget->layout()->setAlignment(Qt::AlignCenter); m_gridWidget->layout()->setContentsMargins(0, 0, 0, 0); @@ -444,7 +409,16 @@ MainWindow::MainWindow(QWidget *parent) : m_gridProgressWidget->hide(); - m_tableWidget->setAlternatingRowColors(true); + m_playlistModel = new PlaylistModel(this); + m_proxyModel = new QSortFilterProxyModel(this); + m_proxyModel->setSourceModel(m_playlistModel); + m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); + + m_tableView->setAlternatingRowColors(true); + m_tableView->setModel(m_proxyModel); + + m_gridView->setItemDelegate(new ThumbnailDelegate(this)); + m_gridView->setModel(m_proxyModel); m_logWidget->setObjectName("logWidget"); @@ -557,10 +531,6 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_loadCoreWindow, SIGNAL(coreLoaded()), this, SLOT(onCoreLoaded())); connect(m_loadCoreWindow, SIGNAL(windowClosed()), this, SLOT(onCoreLoadWindowClosed())); connect(m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(onCurrentListItemChanged(QListWidgetItem*, QListWidgetItem*))); - connect(m_tableWidget, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(onCurrentTableItemChanged(QTableWidgetItem*, QTableWidgetItem*))); - connect(m_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(onContentItemDoubleClicked(QTableWidgetItem*))); - connect(m_tableWidget, SIGNAL(enterPressed()), this, SLOT(onTableWidgetEnterPressed())); - connect(m_tableWidget, SIGNAL(deletePressed()), this, SLOT(onTableWidgetDeletePressed())); connect(m_startCorePushButton, SIGNAL(clicked()), this, SLOT(onStartCoreClicked())); connect(m_coreInfoPushButton, SIGNAL(clicked()), m_coreInfoDialog, SLOT(showCoreInfo())); connect(m_runPushButton, SIGNAL(clicked()), this, SLOT(onRunClicked())); @@ -590,9 +560,25 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_thumbnailPackDownloadProgressDialog, SIGNAL(canceled()), this, SLOT(onThumbnailPackDownloadCanceled())); connect(this, SIGNAL(itemChanged()), this, SLOT(onItemChanged())); - connect(this, SIGNAL(gridItemChanged(QString)), this, SLOT(onGridItemChanged(QString))); connect(this, SIGNAL(gotThumbnailDownload(QString,QString)), this, SLOT(onDownloadThumbnail(QString,QString))); + m_thumbnailTimer->setSingleShot(true); + connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems())); + connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems())); + + connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer())); + + connect(m_gridView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&))); + connect(m_tableView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&))); + + connect(m_gridView->selectionModel(), SIGNAL(currentChanged(const QModelIndex& , const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&))); + connect(m_tableView->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&))); + + connect(m_gridView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&))); + connect(m_tableView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(onContentItemDoubleClicked(const QModelIndex&))); + + connect(m_playlistModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector&)), this, SLOT(onCurrentTableItemDataChanged(const QModelIndex&, const QModelIndex&, const QVector&))); + /* 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); connect(this, SIGNAL(gotStatusMessage(QString,unsigned,unsigned,bool)), this, SLOT(onGotStatusMessage(QString,unsigned,unsigned,bool)), Qt::AutoConnection); @@ -651,8 +637,37 @@ MainWindow::~MainWindow() delete m_thumbnailPixmap2; if (m_thumbnailPixmap3) delete m_thumbnailPixmap3; +} - removeGridItems(); +void MainWindow::startTimer() { + if (m_thumbnailTimer->isActive()) + { + m_thumbnailTimer->stop(); + m_thumbnailTimer->start(50); + } + else + { + m_thumbnailTimer->start(50); + } +} + +void MainWindow::updateVisibleItems() { + if (m_viewType == VIEW_TYPE_ICONS) + { + QVector indexes = m_gridView->visibleIndexes(); + for (int i = 0; i < indexes.size(); i++) + { + m_playlistModel->loadThumbnail(m_proxyModel->mapToSource(indexes.at(i))); + } + } +} + +void MainWindow::setThumbnailCacheLimit(int count) +{ + if (count < 1) + count = 0; + + m_playlistModel->setThumbnailCacheLimit(count); } void MainWindow::onFileSystemDirLoaded(const QString &path) @@ -695,44 +710,11 @@ QVector > MainWindow::getPlaylists() return playlists; } -void MainWindow::onGridItemChanged(QString title) -{ - int i; - - for (i = 0; i < m_gridItems.count(); i++) - { - const QPointer &item = m_gridItems.at(i); - const QHash &hash = item->hash; - - if (hash.value("label_noext") == title) - { - loadImageDeferred(item.data(), item->widget->property("image_path").toString()); - break; - } - } -} - void MainWindow::onItemChanged() { - ViewType viewType = getCurrentViewType(); - - currentItemChanged(getCurrentContentHash()); - - if (viewType == VIEW_TYPE_ICONS) - { - int i; - - for (i = 0; i < m_gridItems.count(); i++) - { - const QPointer &item = m_gridItems.at(i); - - if (item->widget == m_currentGridWidget) - { - loadImageDeferred(item.data(), m_currentGridWidget->property("image_path").toString()); - break; - } - } - } + QModelIndex index = getCurrentContentIndex(); + m_playlistModel->reloadThumbnail(index); + currentItemChanged(index); } QString MainWindow::getSpecialPlaylistPath(SpecialPlaylist playlist) @@ -750,50 +732,6 @@ double MainWindow::lerp(double x, double y, double a, double b, double d) { return a + (b - a) * ((double)(d - x) / (double)(y - x)); } -void MainWindow::onGridItemClicked(ThumbnailWidget *widget) -{ - QHash hash; - ThumbnailWidget *w = static_cast(sender()); - - if (!w) - { - if (widget) - w = widget; - else - return; - } - - if (m_currentGridWidget) - { - m_currentGridWidget->setObjectName("thumbnailWidget"); - m_currentGridWidget->style()->unpolish(m_currentGridWidget); - m_currentGridWidget->style()->polish(m_currentGridWidget); - } - - hash = w->property("hash").value >(); - w->setObjectName("thumbnailWidgetSelected"); - w->style()->unpolish(w); - w->style()->polish(w); - - m_currentGridWidget = w; - 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); @@ -806,35 +744,18 @@ void MainWindow::onListViewClicked() onCurrentListItemChanged(m_listWidget->currentItem(), NULL); } -void MainWindow::calcGridItemSize(GridItem *item, int zoomValue) +void MainWindow::onZoomValueChanged(int zoomValue) { int newSize = 0; - QLabel *label = NULL; 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)); + m_gridView->setGridSize(newSize); - label = item->widget->findChild("thumbnailQLabel"); - - if (label) - setElidedText(label, item->widget, item->widget->layout()->contentsMargins().left() + item->widget->layout()->spacing() + 2, item->labelText); -} - -void MainWindow::onZoomValueChanged(int value) -{ - int i; - - for (i = 0; i < m_gridItems.count(); i++) - { - GridItem *item = m_gridItems.at(i); - calcGridItemSize(item, value); - } - - m_lastZoomSliderValue = value; + m_lastZoomSliderValue = zoomValue; } void MainWindow::showWelcomeScreen() @@ -1237,6 +1158,13 @@ void MainWindow::setTheme(Theme theme) } } +void MainWindow::changeThumbnailType(ThumbnailType type) +{ + m_playlistModel->setThumbnailType(type); + updateVisibleItems(); + m_gridView->viewport()->update(); +} + QVector > MainWindow::getCoreInfo() { QVector > infoList; @@ -1545,66 +1473,7 @@ void MainWindow::onTreeViewItemsSelected(QModelIndexList selectedIndexes) void MainWindow::selectBrowserDir(QString path) { - QStringList horizontal_header_labels; - QDir dir = path; - QStringList dirList; - int i = 0; - - horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); - - /* block this signal because setData() called in addPlaylistHashToTable() would trigger an infinite loop */ - disconnect(m_tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(onCurrentTableItemDataChanged(QTableWidgetItem*))); - - m_tableWidget->clear(); - m_tableWidget->setColumnCount(0); - m_tableWidget->setRowCount(0); - m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); - m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); - m_tableWidget->setSortingEnabled(false); - m_tableWidget->setColumnCount(1); - m_tableWidget->setRowCount(0); - m_tableWidget->setHorizontalHeaderLabels(horizontal_header_labels); - m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); - - dirList = dir.entryList(QDir::NoDotAndDotDot | - QDir::Readable | - QDir::Files | - (m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0)), - QDir::Name); - - if (dirList.count() == 0) - return; - - m_tableWidget->setRowCount(dirList.count()); - - for (i = 0; i < dirList.count(); i++) - { - QString fileName = dirList.at(i); - QTableWidgetItem *item = new QTableWidgetItem(fileName); - QHash hash; - QString filePath(QDir::toNativeSeparators(dir.absoluteFilePath(fileName))); - QFileInfo fileInfo(filePath); - - hash["path"] = filePath; - hash["label"] = hash["path"]; - hash["label_noext"] = fileInfo.completeBaseName(); - hash["db_name"] = fileInfo.dir().dirName(); - - item->setData(Qt::UserRole, QVariant::fromValue >(hash)); - item->setFlags(item->flags() & ~Qt::ItemIsEditable); - - m_tableWidget->setItem(i, 0, item); - } - - m_tableWidget->setSortingEnabled(true); - m_tableWidget->resizeColumnsToContents(); - m_tableWidget->sortByColumn(0, Qt::AscendingOrder); - m_tableWidget->selectRow(0); - - onSearchEnterPressed(); - - connect(m_tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(onCurrentTableItemDataChanged(QTableWidgetItem*))); + m_playlistModel->addDir(path, m_settings->value("show_hidden_files", true).toBool() ? (QDir::Hidden | QDir::System) : static_cast(0)); } QTabWidget* MainWindow::browserAndPlaylistTabWidget() @@ -1612,41 +1481,35 @@ QTabWidget* MainWindow::browserAndPlaylistTabWidget() return m_browserAndPlaylistTabWidget; } -void MainWindow::onTableWidgetEnterPressed() +void MainWindow::onDropWidgetEnterPressed() { #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) /* entry is being renamed, ignore this enter press */ - if (m_tableWidget->isPersistentEditorOpen(m_tableWidget->currentIndex())) + if (m_tableView->isPersistentEditorOpen(m_tableView->currentIndex())) #else /* we can only check if any editor at all is open */ - if (m_tableWidget->isEditorOpen()) + if (m_tableView->isEditorOpen()) #endif return; onRunClicked(); } -void MainWindow::onTableWidgetDeletePressed() +QModelIndex MainWindow::getCurrentContentIndex() { - deleteCurrentPlaylistItem(); + if (m_viewType == VIEW_TYPE_LIST) + { + return m_tableView->currentIndex(); + } + else if (m_viewType == VIEW_TYPE_ICONS) + { + return m_gridView->currentIndex(); + } + return QModelIndex(); } QHash MainWindow::getCurrentContentHash() { - QTableWidgetItem *contentItem = m_tableWidget->currentItem(); - QHash contentHash; - ViewType viewType = getCurrentViewType(); - - if (viewType == VIEW_TYPE_LIST) - { - if (!contentItem) - return contentHash; - - contentHash = contentItem->data(Qt::UserRole).value >(); - } - else if (viewType == VIEW_TYPE_ICONS) - contentHash = m_currentGridHash; - - return contentHash; + return getCurrentContentIndex().data(PlaylistModel::HASH).value >(); } void MainWindow::onContentItemDoubleClicked(QTableWidgetItem*) @@ -1654,6 +1517,12 @@ void MainWindow::onContentItemDoubleClicked(QTableWidgetItem*) onRunClicked(); } +void MainWindow::onContentItemDoubleClicked(const QModelIndex &index) +{ + Q_UNUSED(index); + onRunClicked(); +} + void MainWindow::onStartCoreClicked() { content_ctx_info_t content_info; @@ -1677,13 +1546,12 @@ QHash MainWindow::getSelectedCore() CoreSelection coreSelection = static_cast(coreMap.value("core_selection").toInt()); QHash coreHash; QHash contentHash; - QTableWidgetItem *contentItem = m_tableWidget->currentItem(); ViewType viewType = getCurrentViewType(); - if (viewType == VIEW_TYPE_LIST && contentItem) - contentHash = contentItem->data(Qt::UserRole).value >(); + if (viewType == VIEW_TYPE_LIST) + contentHash = m_tableView->currentIndex().data(PlaylistModel::HASH).value >(); else if (viewType == VIEW_TYPE_ICONS) - contentHash = m_currentGridHash; + contentHash = m_gridView->currentIndex().data(PlaylistModel::HASH).value >(); else return coreHash; @@ -1880,25 +1748,9 @@ void MainWindow::loadContent(const QHash &contentHash) void MainWindow::onRunClicked() { #ifdef HAVE_MENU - QTableWidgetItem *item = m_tableWidget->currentItem(); - ViewType viewType = getCurrentViewType(); - QHash contentHash; + QHash contentHash = getCurrentContentHash(); - if (viewType == VIEW_TYPE_LIST) - { - if (!item) - return; - - contentHash = item->data(Qt::UserRole).value >(); - } - else if (viewType == VIEW_TYPE_ICONS) - { - contentHash = m_currentGridHash; - - if (contentHash.isEmpty()) - return; - } - else + if (contentHash.isEmpty()) return; loadContent(contentHash); @@ -1932,10 +1784,9 @@ ViewOptionsDialog* MainWindow::viewOptionsDialog() void MainWindow::setCoreActions() { - QTableWidgetItem *currentContentItem = m_tableWidget->currentItem(); QListWidgetItem *currentPlaylistItem = m_listWidget->currentItem(); ViewType viewType = getCurrentViewType(); - QHash hash; + QHash hash = getCurrentContentHash(); m_launchWithComboBox->clear(); @@ -1953,11 +1804,6 @@ void MainWindow::setCoreActions() m_launchWithComboBox->addItem(m_currentCore, QVariant::fromValue(comboBoxMap)); } - 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)) { if (!hash.isEmpty()) @@ -2113,10 +1959,6 @@ void MainWindow::onTabWidgetIndexChanged(int index) /* 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); - if (index.isValid()) { m_dirTree->clearSelection(); @@ -2130,10 +1972,6 @@ void MainWindow::onTabWidgetIndexChanged(int index) if (m_lastViewType != getCurrentViewType()) setCurrentViewType(m_lastViewType); - m_tableWidget->clear(); - m_tableWidget->setColumnCount(0); - m_tableWidget->setRowCount(0); - if (item) { m_listWidget->setCurrentItem(NULL); @@ -2167,8 +2005,6 @@ QComboBox* MainWindow::launchWithComboBox() void MainWindow::onSearchLineEditEdited(const QString &text) { int i = 0; - QList items; - QVector > gridItems; QVector textUnicode = text.toUcs4(); QVector textHiraToKata; QVector textKataToHira; @@ -2197,112 +2033,21 @@ void MainWindow::onSearchLineEditEdited(const QString &text) } } - switch(viewType) + if (!foundHira && !foundKata) { - case VIEW_TYPE_LIST: - { - if (text.isEmpty()) - { - for (i = 0; i < m_tableWidget->rowCount(); i++) - { - m_tableWidget->setRowHidden(i, false); - } - return; - } - - items.append(m_tableWidget->findItems(text, Qt::MatchContains)); - - if (foundHira) - { - items.append(m_tableWidget->findItems(QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()), Qt::MatchContains)); - } - - if (foundKata) - { - items.append(m_tableWidget->findItems(QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::MatchContains)); - } - - if (items.isEmpty()) - { - for (i = 0; i < m_tableWidget->rowCount(); i++) - { - m_tableWidget->setRowHidden(i, true); - } - - return; - } - else - { - for (i = 0; i < m_tableWidget->rowCount(); i++) - { - if (items.contains(m_tableWidget->item(i, 0))) - m_tableWidget->setRowHidden(i, false); - else - m_tableWidget->setRowHidden(i, true); - } - } - - break; - } - case VIEW_TYPE_ICONS: - { - int i; - - if (text.isEmpty()) - { - for (i = 0; i < m_gridItems.size(); i++) - { - m_gridItems.at(i)->widget->show(); - } - return; - } - - for (i = 0; i < m_gridItems.count(); i++) - { - const QPointer &item = m_gridItems.at(i); - - if (item->hash.value("label").contains(text, Qt::CaseInsensitive)) - gridItems.append(item); - - if (foundHira) - { - if (item->hash.value("label").contains(QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()), Qt::CaseInsensitive)) - gridItems.append(item); - } - - if (foundKata) - { - if (item->hash.value("label").contains(QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::CaseInsensitive)) - gridItems.append(item); - } - } - - if (gridItems.isEmpty()) - { - for (i = 0; i < m_gridItems.size(); i++) - { - m_gridItems.at(i)->widget->hide(); - } - - return; - } - else - { - for (i = 0; i < m_gridItems.size(); i++) - { - const QPointer &item = m_gridItems.at(i); - - if (gridItems.contains(item)) - item->widget->show(); - else - item->widget->hide(); - } - } - - break; - } - default: - break; + m_proxyModel->setFilterRegExp(QRegExp(text, Qt::CaseInsensitive)); + } + else if (foundHira && !foundKata) + { + m_proxyModel->setFilterRegExp(QRegExp(text + "|" + QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()), Qt::CaseInsensitive)); + } + else if (!foundHira && foundKata) + { + m_proxyModel->setFilterRegExp(QRegExp(text + "|" + QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::CaseInsensitive)); + } + else + { + m_proxyModel->setFilterRegExp(QRegExp(text + "|" + QString::fromUcs4(textHiraToKata.constData(), textHiraToKata.size()) + "|" + QString::fromUcs4(textKataToHira.constData(), textKataToHira.size()), Qt::CaseInsensitive)); } } @@ -2378,39 +2123,19 @@ void MainWindow::onSearchEnterPressed() onSearchLineEditEdited(m_searchLineEdit->text()); } -void MainWindow::onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *) +void MainWindow::onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { - QHash hash; - - if (!current) + if (!roles.contains(Qt::EditRole)) return; - hash = current->data(Qt::UserRole).value >(); - - currentItemChanged(hash); -} - -void MainWindow::onCurrentTableItemDataChanged(QTableWidgetItem *item) -{ - QHash hash; - - if (!item) + if (topLeft != bottomRight) return; - /* block this signal because setData() would trigger an infinite loop here */ - disconnect(m_tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(onCurrentTableItemDataChanged(QTableWidgetItem*))); - - hash = item->data(Qt::UserRole).value >(); - hash["label"] = item->text(); - hash["label_noext"] = QFileInfo(item->text()).completeBaseName(); - - item->setData(Qt::UserRole, QVariant::fromValue(hash)); + QHash hash = topLeft.data(PlaylistModel::HASH).value>(); updateCurrentPlaylistEntry(hash); - currentItemChanged(hash); - - connect(m_tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(onCurrentTableItemDataChanged(QTableWidgetItem*))); + currentItemChanged(topLeft); } void MainWindow::onCurrentListItemDataChanged(QListWidgetItem *item) @@ -2485,7 +2210,7 @@ void MainWindow::renamePlaylistItem(QListWidgetItem *item, QString newName) connect(m_listWidget, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onCurrentListItemDataChanged(QListWidgetItem*))); } -void MainWindow::currentItemChanged(const QHash &hash) +void MainWindow::currentItemChanged(const QModelIndex &index) { settings_t *settings = config_get_ptr(); QString label; @@ -2494,6 +2219,8 @@ void MainWindow::currentItemChanged(const QHash &hash) QString extensionStr; int lastIndex = -1; + const QHash &hash = index.data(PlaylistModel::HASH).value>(); + label = hash["label_noext"]; label.replace(m_fileSanitizerRegex, "_"); @@ -2618,7 +2345,7 @@ void MainWindow::setCurrentViewType(ViewType viewType) { case VIEW_TYPE_ICONS: { - m_tableWidget->hide(); + m_tableView->hide(); m_gridWidget->show(); break; } @@ -2627,48 +2354,48 @@ void MainWindow::setCurrentViewType(ViewType viewType) { m_viewType = VIEW_TYPE_LIST; m_gridWidget->hide(); - m_tableWidget->show(); + m_tableView->show(); break; } } } +void MainWindow::setCurrentThumbnailType(ThumbnailType thumbnailType) +{ + m_lastThumbnailType = m_thumbnailType; + m_thumbnailType = thumbnailType; + + m_playlistModel->setThumbnailType(thumbnailType); + updateVisibleItems(); + m_gridView->viewport()->update(); +} + MainWindow::ViewType MainWindow::getCurrentViewType() { return m_viewType; } +ThumbnailType MainWindow::getCurrentThumbnailType() +{ + return m_thumbnailType; +} + 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; - switch (viewType) - { - case VIEW_TYPE_ICONS: - { - initContentGridLayout(); - break; - } - case VIEW_TYPE_LIST: - default: - { - initContentTableWidget(); - break; - } - } + initContentTableWidget(); setCoreActions(); } -TableWidget* MainWindow::contentTableWidget() +TableView* MainWindow::contentTableView() { - return m_tableWidget; + return m_tableView; } QWidget* MainWindow::contentGridWidget() @@ -2676,9 +2403,9 @@ QWidget* MainWindow::contentGridWidget() return m_gridWidget; } -FlowLayout* MainWindow::contentGridLayout() +GridView* MainWindow::contentGridView() { - return m_gridLayout; + return m_gridView; } void MainWindow::onBrowserDownloadsClicked() @@ -2883,179 +2610,6 @@ void MainWindow::onLoadCoreClicked(const QStringList &extensionFilters) m_loadCoreWindow->initCoreList(extensionFilters); } -void MainWindow::removeGridItems() -{ - if (m_gridItems.count() > 0) - { - QMutableVectorIterator > items(m_gridItems); - - m_pendingItemUpdates.clear(); - - while (items.hasNext()) - { - QPointer item = items.next(); - - if (item) - { - item->imageWatcher.waitForFinished(); - - items.remove(); - - m_gridLayout->removeWidget(item->widget); - - delete item->widget; - delete item; - } - } - } -} - -void MainWindow::onDeferredImageLoaded() -{ - const QFutureWatcher *watcher = static_cast*>(sender()); - GridItem *item = NULL; - - if (!watcher) - return; - - item = watcher->result(); - - if (!item) - return; - - if (m_gridItems.contains(item)) - { - if (!item->image.isNull()) - { - m_pendingItemUpdates.append(item); - QTimer::singleShot(0, this, SLOT(onPendingItemUpdates())); - } - } -} - -void MainWindow::onPendingItemUpdates() -{ - QMutableListIterator list(m_pendingItemUpdates); - - while (list.hasNext()) - { - GridItem *item = list.next(); - - if (!item) - continue; - - if (m_gridItems.contains(item)) - onUpdateGridItemPixmapFromImage(item); - - list.remove(); - } -} - -void MainWindow::onUpdateGridItemPixmapFromImage(GridItem *item) -{ - if (!item) - return; - - if (!m_gridItems.contains(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); - item->imageWatcher.setFuture(QtConcurrent::run(this, &MainWindow::doDeferredImageLoad, item, path)); -} - -GridItem* MainWindow::doDeferredImageLoad(GridItem *item, QString path) -{ - /* this runs in another thread */ - if (!item) - return NULL; - - /* 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); - - return item; -} - -void MainWindow::initContentGridLayout() -{ - QListWidgetItem *item = m_listWidget->currentItem(); - QString path; - - if (!item) - return; - - m_gridProgressBar->setMinimum(0); - m_gridProgressBar->setMaximum(0); - m_gridProgressBar->setValue(0); - m_gridProgressWidget->show(); - - removeGridItems(); - - m_currentGridHash.clear(); - - if (m_currentGridWidget) - { - m_currentGridWidget->setObjectName("thumbnailWidget"); - m_currentGridWidget->style()->unpolish(m_currentGridWidget); - m_currentGridWidget->style()->polish(m_currentGridWidget); - } - - m_currentGridWidget = NULL; - - path = item->data(Qt::UserRole).toString(); - - if (path == ALL_PLAYLISTS_TOKEN) - { - settings_t *settings = config_get_ptr(); - QDir playlistDir(settings->paths.directory_playlist); - QStringList playlists; - int i = 0; - - for (i = 0; i < m_playlistFiles.count(); i++) - { - const QString &playlist = m_playlistFiles.at(i); - playlists.append(playlistDir.absoluteFilePath(playlist)); - } - - addPlaylistItemsToGrid(playlists, true); - } - else - addPlaylistItemsToGrid(QStringList() << path); - - QTimer::singleShot(0, this, SLOT(onContentGridInited())); -} - -void MainWindow::onContentGridInited() -{ - ThumbnailWidget *thumbnailWidget = NULL; - - m_gridLayoutWidget->resize(m_gridScrollArea->viewport()->size()); - - onZoomValueChanged(m_zoomSlider->value()); - - onSearchEnterPressed(); - - if (m_gridItems.count() > 0) - { - GridItem *gridItem = m_gridItems.at(0); - - if (gridItem) - { - thumbnailWidget = m_gridItems.at(0)->widget.data(); - - if (thumbnailWidget) - onGridItemClicked(thumbnailWidget); - } - } -} - void MainWindow::initContentTableWidget() { QListWidgetItem *item = m_listWidget->currentItem(); @@ -3077,23 +2631,12 @@ void MainWindow::initContentTableWidget() m_currentGridWidget = NULL; - horizontal_header_labels << msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_NAME); - /* block this signal because setData() called in addPlaylistHashToTable() would trigger an infinite loop */ - disconnect(m_tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(onCurrentTableItemDataChanged(QTableWidgetItem*))); - - m_tableWidget->clear(); - m_tableWidget->setColumnCount(0); - m_tableWidget->setRowCount(0); - m_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); - m_tableWidget->setSelectionMode(QAbstractItemView::SingleSelection); - m_tableWidget->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed); - m_tableWidget->setSortingEnabled(false); - m_tableWidget->setColumnCount(1); - m_tableWidget->setRowCount(0); - m_tableWidget->setHorizontalHeaderLabels(horizontal_header_labels); - m_tableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_tableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); + m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); + m_tableView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed); + m_tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_tableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); path = item->data(Qt::UserRole).toString(); @@ -3110,31 +2653,24 @@ void MainWindow::initContentTableWidget() playlists.append(playlistDir.absoluteFilePath(playlist)); } - addPlaylistItemsToTable(playlists, true); + m_playlistModel->addPlaylistItems(playlists, true); } else - addPlaylistItemsToTable(QStringList() << path); + m_playlistModel->addPlaylistItems(QStringList() << path); - m_tableWidget->setSortingEnabled(true); + //TODO delete? + //m_tableView->setSortingEnabled(true); if (item != m_historyPlaylistsItem) - m_tableWidget->sortByColumn(0, Qt::AscendingOrder); + m_tableView->sortByColumn(0, Qt::AscendingOrder); - m_tableWidget->resizeColumnsToContents(); + m_tableView->resizeColumnsToContents(); - for (i = 0; i < m_tableWidget->rowCount(); i++) - { - /* select the first non-hidden row */ - if (!m_tableWidget->isRowHidden(i)) - { - m_tableWidget->selectRow(i); - break; - } - } - - onSearchEnterPressed(); - - connect(m_tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(onCurrentTableItemDataChanged(QTableWidgetItem*))); + QModelIndex index = m_proxyModel->index(0, 0); + m_gridView->scrollToTop(); + m_gridView->setCurrentIndex(index); + m_tableView->setCurrentIndex(index); + currentItemChanged(index); } void MainWindow::keyPressEvent(QKeyEvent *event) @@ -3174,6 +2710,40 @@ QString MainWindow::getCurrentViewTypeString() return QStringLiteral("list"); } +QString MainWindow::getCurrentThumbnailTypeString() +{ + switch (m_thumbnailType) + { + case THUMBNAIL_TYPE_SCREENSHOT: + { + return QStringLiteral("screenshot"); + } + case THUMBNAIL_TYPE_TITLE_SCREEN: + { + return QStringLiteral("title"); + } + case THUMBNAIL_TYPE_BOXART: + default: + { + return QStringLiteral("boxart"); + } + } + + return QStringLiteral("list"); +} + +ThumbnailType MainWindow::getThumbnailTypeFromString(QString thumbnailType) +{ + if (thumbnailType == "boxart") + return THUMBNAIL_TYPE_BOXART; + else if (thumbnailType == "screenshot") + return THUMBNAIL_TYPE_SCREENSHOT; + else if (thumbnailType == "title") + return THUMBNAIL_TYPE_TITLE_SCREEN; + + return THUMBNAIL_TYPE_BOXART; +} + void MainWindow::closeEvent(QCloseEvent *event) { if (m_settings->value("save_geometry", false).toBool()) diff --git a/ui/drivers/qt/viewoptionsdialog.cpp b/ui/drivers/qt/viewoptionsdialog.cpp index 5d9bce0466..dcfa80782d 100644 --- a/ui/drivers/qt/viewoptionsdialog.cpp +++ b/ui/drivers/qt/viewoptionsdialog.cpp @@ -19,7 +19,7 @@ extern "C" { } ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : - QDialog(parent) + QDialog(mainwindow) ,m_mainwindow(mainwindow) ,m_settings(mainwindow->settings()) ,m_saveGeometryCheckBox(new QCheckBox(this)) @@ -27,6 +27,8 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : ,m_saveLastTabCheckBox(new QCheckBox(this)) ,m_showHiddenFilesCheckBox(new QCheckBox(this)) ,m_themeComboBox(new QComboBox(this)) + ,m_thumbnailComboBox(new QComboBox(this)) + ,m_thumbnailCacheSpinBox(new QSpinBox(this)) ,m_startupPlaylistComboBox(new QComboBox(this)) ,m_highlightColorPushButton(new QPushButton(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_CHOOSE), this)) ,m_highlightColor() @@ -45,6 +47,13 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_DARK), MainWindow::THEME_DARK); m_themeComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME_CUSTOM), MainWindow::THEME_CUSTOM); + m_thumbnailComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_BOXART), THUMBNAIL_TYPE_BOXART); + m_thumbnailComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_SCREENSHOT), THUMBNAIL_TYPE_SCREENSHOT); + m_thumbnailComboBox->addItem(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_THUMBNAIL_TITLE_SCREEN), THUMBNAIL_TYPE_TITLE_SCREEN); + + m_thumbnailCacheSpinBox->setSuffix(" MB"); + m_thumbnailCacheSpinBox->setRange(0, 99999); + m_allPlaylistsListMaxCountSpinBox->setRange(0, 99999); m_allPlaylistsGridMaxCountSpinBox->setRange(0, 99999); @@ -67,6 +76,8 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_LIST_MAX_COUNT), m_allPlaylistsListMaxCountSpinBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_ALL_PLAYLISTS_GRID_MAX_COUNT), m_allPlaylistsGridMaxCountSpinBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_STARTUP_PLAYLIST), m_startupPlaylistComboBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_TYPE), m_thumbnailComboBox); + form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THUMBNAIL_CACHE_LIMIT), m_thumbnailCacheSpinBox); form->addRow(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_OPTIONS_THEME), m_themeComboBox); form->addRow(m_highlightColorLabel, m_highlightColorPushButton); @@ -77,9 +88,16 @@ ViewOptionsDialog::ViewOptionsDialog(MainWindow *mainwindow, QWidget *parent) : loadViewOptions(); connect(m_themeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThemeComboBoxIndexChanged(int))); + connect(m_thumbnailComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onThumbnailComboBoxIndexChanged(int))); connect(m_highlightColorPushButton, SIGNAL(clicked()), this, SLOT(onHighlightColorChoose())); } +void ViewOptionsDialog::onThumbnailComboBoxIndexChanged(int index) +{ + ThumbnailType type = static_cast(m_thumbnailComboBox->currentData().value()); + m_mainwindow->setCurrentThumbnailType(type); +} + void ViewOptionsDialog::onThemeComboBoxIndexChanged(int) { MainWindow::Theme theme = static_cast(m_themeComboBox->currentData(Qt::UserRole).toInt()); @@ -138,6 +156,7 @@ void ViewOptionsDialog::loadViewOptions() QVector > playlists = m_mainwindow->getPlaylists(); QString initialPlaylist = m_settings->value("initial_playlist", m_mainwindow->getSpecialPlaylistPath(SPECIAL_PLAYLIST_HISTORY)).toString(); int themeIndex = 0; + int thumbnailIndex = 0; int playlistIndex = 0; int i; @@ -148,12 +167,18 @@ void ViewOptionsDialog::loadViewOptions() m_suggestLoadedCoreFirstCheckBox->setChecked(m_settings->value("suggest_loaded_core_first", false).toBool()); m_allPlaylistsListMaxCountSpinBox->setValue(m_settings->value("all_playlists_list_max_count", 0).toInt()); m_allPlaylistsGridMaxCountSpinBox->setValue(m_settings->value("all_playlists_grid_max_count", 5000).toInt()); + m_thumbnailCacheSpinBox->setValue(m_settings->value("thumbnail_cache_limit", 512).toInt()); themeIndex = m_themeComboBox->findData(m_mainwindow->getThemeFromString(m_settings->value("theme", "default").toString())); if (m_themeComboBox->count() > themeIndex) m_themeComboBox->setCurrentIndex(themeIndex); + thumbnailIndex = m_thumbnailComboBox->findData(m_mainwindow->getThumbnailTypeFromString(m_settings->value("icon_view_thumbnail_type", "boxart").toString())); + + if (m_thumbnailComboBox->count() > thumbnailIndex) + m_thumbnailComboBox->setCurrentIndex(thumbnailIndex); + if (highlightColor.isValid()) { m_highlightColor = highlightColor; @@ -204,12 +229,15 @@ void ViewOptionsDialog::saveViewOptions() m_settings->setValue("all_playlists_list_max_count", m_allPlaylistsListMaxCountSpinBox->value()); m_settings->setValue("all_playlists_grid_max_count", m_allPlaylistsGridMaxCountSpinBox->value()); m_settings->setValue("initial_playlist", m_startupPlaylistComboBox->currentData(Qt::UserRole).toString()); + m_settings->setValue("icon_view_thumbnail_type", m_mainwindow->getCurrentThumbnailTypeString()); + m_settings->setValue("thumbnail_cache_limit", m_thumbnailCacheSpinBox->value()); if (!m_mainwindow->customThemeString().isEmpty()) m_settings->setValue("custom_theme", m_customThemePath); m_mainwindow->setAllPlaylistsListMaxCount(m_allPlaylistsListMaxCountSpinBox->value()); m_mainwindow->setAllPlaylistsGridMaxCount(m_allPlaylistsGridMaxCountSpinBox->value()); + m_mainwindow->setThumbnailCacheLimit(m_thumbnailCacheSpinBox->value()); } void ViewOptionsDialog::onAccepted() @@ -229,11 +257,12 @@ void ViewOptionsDialog::onRejected() void ViewOptionsDialog::showDialog() { loadViewOptions(); + setWindowFlags(windowFlags() | Qt::Tool); show(); + activateWindow(); } void ViewOptionsDialog::hideDialog() { reject(); } - diff --git a/ui/drivers/qt/viewoptionsdialog.h b/ui/drivers/qt/viewoptionsdialog.h index faf0f143df..4e9ff4c76a 100644 --- a/ui/drivers/qt/viewoptionsdialog.h +++ b/ui/drivers/qt/viewoptionsdialog.h @@ -24,6 +24,7 @@ public slots: void onRejected(); private slots: void onThemeComboBoxIndexChanged(int index); + void onThumbnailComboBoxIndexChanged(int index); void onHighlightColorChoose(); private: void loadViewOptions(); @@ -37,6 +38,8 @@ private: QCheckBox *m_saveLastTabCheckBox; QCheckBox *m_showHiddenFilesCheckBox; QComboBox *m_themeComboBox; + QComboBox *m_thumbnailComboBox; + QSpinBox *m_thumbnailCacheSpinBox; QComboBox *m_startupPlaylistComboBox; QPushButton *m_highlightColorPushButton; QColor m_highlightColor; diff --git a/ui/drivers/ui_qt.cpp b/ui/drivers/ui_qt.cpp index 1c494f1e99..306143527a 100644 --- a/ui/drivers/ui_qt.cpp +++ b/ui/drivers/ui_qt.cpp @@ -293,11 +293,12 @@ static void* ui_companion_qt_init(void) widget->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(widget, SIGNAL(filesDropped(QStringList)), mainwindow, SLOT(onPlaylistFilesDropped(QStringList))); + QObject::connect(widget, SIGNAL(enterPressed()), mainwindow, SLOT(onDropWidgetEnterPressed())); QObject::connect(widget, SIGNAL(deletePressed()), mainwindow, SLOT(deleteCurrentPlaylistItem())); QObject::connect(widget, SIGNAL(customContextMenuRequested(const QPoint&)), mainwindow, SLOT(onFileDropWidgetContextMenuRequested(const QPoint&))); layout = new QVBoxLayout(); - layout->addWidget(mainwindow->contentTableWidget()); + layout->addWidget(mainwindow->contentTableView()); layout->addWidget(mainwindow->contentGridWidget()); widget->setLayout(layout); @@ -513,6 +514,11 @@ static void* ui_companion_qt_init(void) if (qsettings->contains("all_playlists_grid_max_count")) mainwindow->setAllPlaylistsGridMaxCount(qsettings->value("all_playlists_grid_max_count", 5000).toInt()); + if (qsettings->contains("thumbnail_cache_limit")) + mainwindow->setThumbnailCacheLimit(qsettings->value("thumbnail_cache_limit", 500).toInt()); + else + mainwindow->setThumbnailCacheLimit(500); + if (qsettings->contains("geometry")) if (qsettings->contains("save_geometry")) mainwindow->restoreGeometry(qsettings->value("geometry").toByteArray()); @@ -555,6 +561,25 @@ static void* ui_companion_qt_init(void) else mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST); + if (qsettings->contains("icon_view_thumbnail_type")) + { + QString thumbnailType = qsettings->value("icon_view_thumbnail_type", "boxart").toString(); + + if (thumbnailType == "boxart") + mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART); + else if (thumbnailType == "screenshot") + mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_SCREENSHOT); + else if (thumbnailType == "title") + mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_TITLE_SCREEN); + else + mainwindow->setCurrentThumbnailType(THUMBNAIL_TYPE_BOXART); + + /* we set it to the same thing a second time so that m_lastThumbnailType is also equal to the startup view type */ + mainwindow->setCurrentThumbnailType(mainwindow->getCurrentThumbnailType()); + } + else + mainwindow->setCurrentViewType(MainWindow::VIEW_TYPE_LIST); + /* We make sure to hook up the tab widget callback only after the tabs themselves have been added, * but before changing to a specific one, to avoid the callback firing before the view type is set. */ diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 7c050d075b..70eb1810e1 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -38,6 +38,10 @@ #include #include #include +#include +#include +#include +#include extern "C" { #include @@ -84,7 +88,7 @@ class LoadCoreWindow; class MainWindow; class ThumbnailWidget; class ThumbnailLabel; -class FlowLayout; +class GridView; class ShaderParamsDialog; class CoreOptionsDialog; class CoreInfoDialog; @@ -96,19 +100,58 @@ enum SpecialPlaylist SPECIAL_PLAYLIST_HISTORY }; -class GridItem : public QObject +enum ThumbnailType +{ + THUMBNAIL_TYPE_BOXART, + THUMBNAIL_TYPE_SCREENSHOT, + THUMBNAIL_TYPE_TITLE_SCREEN, +}; + +class PlaylistModel : public QAbstractListModel { Q_OBJECT -public: - GridItem(); - QPointer widget; - QPointer label; - QHash hash; - QImage image; - QPixmap pixmap; - QFutureWatcher imageWatcher; - QString labelText; +public: + enum Roles + { + HASH = Qt::UserRole + 1, + THUMBNAIL + }; + + PlaylistModel(QObject *parent = 0); + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + void addPlaylistItems(const QStringList &paths, bool add = false); + void addDir(QString path, QFlags showHidden); + void setThumbnailType(const ThumbnailType type); + void loadThumbnail(const QModelIndex &index); + void reloadThumbnail(const QModelIndex &index); + void reloadThumbnailPath(const QString path); + void reloadSystemThumbnails(const QString system); + void setThumbnailCacheLimit(int limit); + +signals: + void imageLoaded(const QImage image, const QModelIndex &index, const QString &path); + +private slots: + void onImageLoaded(const QImage image, const QModelIndex &index, const QString &path); + +private: + QVector > m_contents; + QCache m_cache; + QSet m_pendingImages; + QVector m_imageFormats; + QRegularExpression m_fileSanitizerRegex; + ThumbnailType m_thumbnailType = THUMBNAIL_TYPE_BOXART; + QString getThumbnailPath(const QModelIndex &index, QString type) const; + QString getCurrentTypeThumbnailPath(const QModelIndex &index) const; + void getPlaylistItems(QString path); + void loadImage(const QModelIndex &index, const QString &path); }; class ThumbnailWidget : public QFrame @@ -164,17 +207,12 @@ protected slots: void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); }; -class TableWidget : public QTableWidget +class TableView : public QTableView { Q_OBJECT public: - TableWidget(QWidget *parent = 0); + TableView(QWidget *parent = 0); bool isEditorOpen(); -signals: - void enterPressed(); - void deletePressed(); -protected: - void keyPressEvent(QKeyEvent *event); }; class ListWidget : public QListWidget @@ -263,9 +301,10 @@ public: MainWindow(QWidget *parent = NULL); ~MainWindow(); TreeView* dirTreeView(); + PlaylistModel* playlistModel(); ListWidget* playlistListWidget(); - TableWidget* contentTableWidget(); - FlowLayout* contentGridLayout(); + TableView* contentTableView(); + GridView* contentGridView(); QWidget* contentGridWidget(); QWidget* searchWidget(); QLineEdit* searchLineEdit(); @@ -289,15 +328,20 @@ public: bool setCustomThemeFile(QString filePath); void setCustomThemeString(QString qss); const QString& customThemeString() const; - GridItem* doDeferredImageLoad(GridItem *item, QString path); void setCurrentViewType(ViewType viewType); QString getCurrentViewTypeString(); ViewType getCurrentViewType(); + void setCurrentThumbnailType(ThumbnailType thumbnailType); + QString getCurrentThumbnailTypeString(); + ThumbnailType getCurrentThumbnailType(); + ThumbnailType getThumbnailTypeFromString(QString thumbnailType); void setAllPlaylistsListMaxCount(int count); void setAllPlaylistsGridMaxCount(int count); + void setThumbnailCacheLimit(int count); PlaylistEntryDialog* playlistEntryDialog(); void addFilesToPlaylist(QStringList files); QString getCurrentPlaylistPath(); + QModelIndex getCurrentContentIndex(); QHash getCurrentContentHash(); static double lerp(double x, double y, double a, double b, double d); QString getSpecialPlaylistPath(SpecialPlaylist playlist); @@ -317,6 +361,7 @@ signals: void showInfoMessageDeferred(QString msg); void extractArchiveDeferred(QString path, QString extractionDir, QString tempExtension, retro_task_callback_t cb); void itemChanged(); + void updateThumbnails(); void gridItemChanged(QString title); void gotThumbnailDownload(QString system, QString title); void scrollToDownloads(QString path); @@ -327,15 +372,13 @@ public slots: void onBrowserUpClicked(); void onBrowserStartClicked(); void initContentTableWidget(); - void initContentGridLayout(); void onViewClosedDocksAboutToShow(); void onShowHiddenDockWidgetAction(); void setCoreActions(); void onRunClicked(); void loadContent(const QHash &contentHash); void onStartCoreClicked(); - void onTableWidgetEnterPressed(); - void onTableWidgetDeletePressed(); + void onDropWidgetEnterPressed(); void selectBrowserDir(QString path); void resizeThumbnails(bool one, bool two, bool three); void onResizeThumbnailOne(); @@ -365,23 +408,20 @@ public slots: void downloadAllThumbnails(QString system, QUrl url = QUrl()); void downloadPlaylistThumbnails(QString playlistPath); void downloadNextPlaylistThumbnail(QString system, QString title, QString type, QUrl url = QUrl()); + void changeThumbnailType(ThumbnailType type); private slots: void onLoadCoreClicked(const QStringList &extensionFilters = QStringList()); void onUnloadCoreMenuAction(); void onTimeout(); void onCoreLoaded(); + void onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles); void onCurrentListItemChanged(QListWidgetItem *current, QListWidgetItem *previous); - void onCurrentTableItemChanged(QTableWidgetItem *current, QTableWidgetItem *previous); - void onCurrentTableItemDataChanged(QTableWidgetItem *item); void onCurrentListItemDataChanged(QListWidgetItem *item); - void currentItemChanged(const QHash &hash); + void currentItemChanged(const QModelIndex &index); void onSearchEnterPressed(); void onSearchLineEditEdited(const QString &text); - void addPlaylistItemsToTable(const QStringList &paths, bool all = false); - void addPlaylistHashToTable(const QVector > &items); - void addPlaylistItemsToGrid(const QStringList &paths, bool all = false); - void addPlaylistHashToGrid(const QVector > &items); + void onContentItemDoubleClicked(const QModelIndex &index); void onContentItemDoubleClicked(QTableWidgetItem *item); void onCoreLoadWindowClosed(); void onTreeViewItemsSelected(QModelIndexList selectedIndexes); @@ -390,13 +430,7 @@ private slots: void onFileBrowserTreeContextMenuRequested(const QPoint &pos); void onPlaylistWidgetContextMenuRequested(const QPoint &pos); void onStopClicked(); - void onDeferredImageLoaded(); void onZoomValueChanged(int value); - void onContentGridInited(); - void onUpdateGridItemPixmapFromImage(GridItem *item); - void onPendingItemUpdates(); - void onGridItemDoubleClicked(); - void onGridItemClicked(ThumbnailWidget *thumbnailWidget = NULL); void onPlaylistFilesDropped(QStringList files); void onShaderParamsClicked(); void onCoreOptionsClicked(); @@ -404,7 +438,6 @@ private slots: void onShowInfoMessage(QString msg); void onContributorsClicked(); void onItemChanged(); - void onGridItemChanged(QString title); void onFileSystemDirLoaded(const QString &path); void onDownloadScroll(QString path); void onDownloadScrollAgain(QString path); @@ -439,14 +472,14 @@ private slots: void onPlaylistThumbnailDownloadReadyRead(); void onPlaylistThumbnailDownloadCanceled(); + void startTimer(); + void updateVisibleItems(); + private: void setCurrentCoreLabel(); void getPlaylistFiles(); bool isCoreLoaded(); bool isContentLessCore(); - void removeGridItems(); - void loadImageDeferred(GridItem *item, QString path); - void calcGridItemSize(GridItem *item, int zoomValue); bool updateCurrentPlaylistEntry(const QHash &contentHash); int extractArchive(QString path); void removeUpdateTempFiles(); @@ -454,8 +487,9 @@ private: void renamePlaylistItem(QListWidgetItem *item, QString newName); bool currentPlaylistIsSpecial(); bool currentPlaylistIsAll(); - QVector > getPlaylistItems(QString pathString); + PlaylistModel *m_playlistModel; + QSortFilterProxyModel *m_proxyModel; LoadCoreWindow *m_loadCoreWindow; QTimer *m_timer; QString m_currentCore; @@ -464,7 +498,7 @@ private: TreeView *m_dirTree; QFileSystemModel *m_dirModel; ListWidget *m_listWidget; - TableWidget *m_tableWidget; + TableView *m_tableView; QWidget *m_searchWidget; QLineEdit *m_searchLineEdit; QDockWidget *m_searchDock; @@ -496,19 +530,19 @@ private: QListWidgetItem *m_historyPlaylistsItem; QIcon m_folderIcon; QString m_customThemeString; - FlowLayout *m_gridLayout; + GridView *m_gridView; QWidget *m_gridWidget; QScrollArea *m_gridScrollArea; - QVector > m_gridItems; QWidget *m_gridLayoutWidget; QSlider *m_zoomSlider; int m_lastZoomSliderValue; - QList m_pendingItemUpdates; ViewType m_viewType; + ThumbnailType m_thumbnailType; QProgressBar *m_gridProgressBar; QWidget *m_gridProgressWidget; QHash m_currentGridHash; ViewType m_lastViewType; + ThumbnailType m_lastThumbnailType; QPointer m_currentGridWidget; int m_allPlaylistsListMaxCount; int m_allPlaylistsGridMaxCount; @@ -540,6 +574,8 @@ private: bool m_playlistThumbnailDownloadWasCanceled; QString m_pendingDirScrollPath; + QTimer *m_thumbnailTimer; + protected: void closeEvent(QCloseEvent *event); void keyPressEvent(QKeyEvent *event); From 240856f19c0adcb77af8f6ac7e6aea41f8e458fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20L=C3=B3pez?= Date: Wed, 12 Dec 2018 20:45:19 +0100 Subject: [PATCH 03/13] Qt: Implement custom playlist model and grid view. Only load images when they become visible and cache them. Add option to change thumbnail type displayed in grid view. Add option to change thumbnail cache limit. --- intl/msg_hash_us.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 41f76c5d83..040d8ae696 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -7821,8 +7821,6 @@ MSG_HASH( MSG_HASH( MSG_MISSING_ASSETS, "Warning: Missing assets, use the Online Updater if available" - ) -MSG_HASH( - MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SAVE_POSITION, - "Remember Window Position and Size" - ) +) +MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SAVE_POSITION, + "Remember Window Position and Size") From 20900463f4532a41589f640686231b13d487ff88 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Fri, 14 Dec 2018 23:16:24 +0100 Subject: [PATCH 04/13] Qt: fix crash when switching playlists --- ui/drivers/qt/gridview.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp index 5ba83ba1a8..de6948a8e9 100644 --- a/ui/drivers/qt/gridview.cpp +++ b/ui/drivers/qt/gridview.cpp @@ -253,6 +253,7 @@ void GridView::resizeEvent(QResizeEvent*) void GridView::reset() { + m_visibleIndexes.clear(); QAbstractItemView::reset(); refresh(); } From b6f36fcf40d322e6155e6089f9e4a880ac1f8f41 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Fri, 14 Dec 2018 23:19:53 +0100 Subject: [PATCH 05/13] Qt: enable table sorting and hide vertical header --- 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 e7e05bf41b..ccec679b9d 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -416,6 +416,8 @@ MainWindow::MainWindow(QWidget *parent) : m_tableView->setAlternatingRowColors(true); m_tableView->setModel(m_proxyModel); + m_tableView->setSortingEnabled(true); + m_tableView->verticalHeader()->setVisible(false); m_gridView->setItemDelegate(new ThumbnailDelegate(this)); m_gridView->setModel(m_proxyModel); From f5840ae6f23cb22f4c9d3813d0664734a1e21036 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Sun, 16 Dec 2018 01:20:11 +0100 Subject: [PATCH 06/13] Qt: Don't sort history playlist. Declare variables at top. --- ui/drivers/qt/gridview.cpp | 54 ++++++++++++++++++++-------------- ui/drivers/qt/ui_qt_window.cpp | 13 ++++---- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp index de6948a8e9..e9e74c8128 100644 --- a/ui/drivers/qt/gridview.cpp +++ b/ui/drivers/qt/gridview.cpp @@ -13,22 +13,20 @@ ThumbnailDelegate::ThumbnailDelegate(QObject* parent) : void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const { - painter->save(); - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - const QWidget *widget = opt.widget; - QStyle *style = widget->style(); - int margin = 11; int textMargin = 4; - QRect rect = opt.rect; int textHeight = painter->fontMetrics().height() + margin + margin; + QRect rect = opt.rect; QRect adjusted = rect.adjusted(margin, margin, -margin, -textHeight + textMargin); QPixmap pixmap = index.data(PlaylistModel::THUMBNAIL).value(); + painter->save(); + + initStyleOption(&opt, index); + // draw the background style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); @@ -43,6 +41,8 @@ void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &opt if (!opt.text.isEmpty()) { QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + QRect textRect = QRect(rect.x() + margin, rect.y() + adjusted.height() - textMargin + margin, rect.width() - 2 * margin, textHeight); + QString elidedText = painter->fontMetrics().elidedText(opt.text, opt.textElideMode, textRect.width(), Qt::TextShowMnemonic); if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) cg = QPalette::Inactive; @@ -52,9 +52,6 @@ void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &opt else painter->setPen(opt.palette.color(cg, QPalette::Text)); - QRect textRect = QRect(rect.x() + margin, rect.y() + adjusted.height() - textMargin + margin, rect.width() - 2 * margin, textHeight); - QString elidedText = painter->fontMetrics().elidedText(opt.text, opt.textElideMode, textRect.width(), Qt::TextShowMnemonic); - painter->setFont(opt.font); painter->drawText(textRect, Qt::AlignCenter, elidedText); } @@ -174,8 +171,9 @@ QRect GridView::visualRect(const QModelIndex &index) const QRectF GridView::viewportRectForRow(int row) const { + QRectF rect; calculateRectsIfNecessary(); - QRectF rect = m_rectForRow.value(row).toRect(); + rect = m_rectForRow.value(row).toRect(); if (!rect.isValid()) return rect; return QRectF(rect.x() - horizontalScrollBar()->value(), rect.y() - verticalScrollBar()->value(), rect.width(), rect.height()); @@ -200,15 +198,19 @@ void GridView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint) QModelIndex GridView::indexAt(const QPoint &point_) const { QPoint point(point_); + QHash::const_iterator i; point.rx() += horizontalScrollBar()->value(); point.ry() += verticalScrollBar()->value(); + calculateRectsIfNecessary(); - QHashIterator i(m_rectForRow); - while (i.hasNext()) + + i = m_rectForRow.constBegin(); + + while (i != m_rectForRow.constEnd()) { - i.next(); if (i.value().contains(point)) return model()->index(i.key(), 0, rootIndex()); + i++; } return QModelIndex(); } @@ -302,23 +304,28 @@ QVector GridView::visibleIndexes() const { void GridView::setSelection(const QRect &rect, QFlags flags) { - QRect rectangle = rect.translated(horizontalScrollBar()->value(), verticalScrollBar()->value()).normalized(); - calculateRectsIfNecessary(); - QHashIterator i(m_rectForRow); + QRect rectangle; + QHash::const_iterator i; int firstRow = model()->rowCount(); int lastRow = -1; - while (i.hasNext()) + + calculateRectsIfNecessary(); + + rectangle = rect.translated(horizontalScrollBar()->value(), verticalScrollBar()->value()).normalized(); + + i = m_rectForRow.constBegin(); + while (i != m_rectForRow.constEnd()) { - i.next(); if (i.value().intersects(rectangle)) { firstRow = firstRow < i.key() ? firstRow : i.key(); lastRow = lastRow > i.key() ? lastRow : i.key(); } + i++; } if (firstRow != model()->rowCount() && lastRow != -1) { - QItemSelection selection( model()->index(firstRow, 0, rootIndex()), model()->index(lastRow, 0, rootIndex())); + QItemSelection selection(model()->index(firstRow, 0, rootIndex()), model()->index(lastRow, 0, rootIndex())); selectionModel()->select(selection, flags); } else @@ -332,7 +339,9 @@ void GridView::setSelection(const QRect &rect, QFlagsindex(row, 0, rootIndex()); QRectF rect = viewportRectForRow(row); + QStyleOptionViewItem option = viewOptions(); if (!rect.isValid() || rect.bottom() < 0 || rect.y() > viewport()->height()) continue; m_visibleIndexes.append(index); - QStyleOptionViewItem option = viewOptions(); option.rect = rect.toRect(); if (selectionModel()->isSelected(index)) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index ccec679b9d..c767a414d6 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -2127,13 +2127,15 @@ void MainWindow::onSearchEnterPressed() void MainWindow::onCurrentTableItemDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { + QHash hash; + if (!roles.contains(Qt::EditRole)) return; if (topLeft != bottomRight) return; - QHash hash = topLeft.data(PlaylistModel::HASH).value>(); + hash = topLeft.data(PlaylistModel::HASH).value>(); updateCurrentPlaylistEntry(hash); @@ -2617,6 +2619,7 @@ void MainWindow::initContentTableWidget() QListWidgetItem *item = m_listWidget->currentItem(); QStringList horizontal_header_labels; QString path; + QModelIndex index; int i = 0; if (!item) @@ -2633,7 +2636,6 @@ void MainWindow::initContentTableWidget() m_currentGridWidget = NULL; - m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); m_tableView->setSelectionMode(QAbstractItemView::SingleSelection); m_tableView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::EditKeyPressed); @@ -2660,15 +2662,14 @@ void MainWindow::initContentTableWidget() else m_playlistModel->addPlaylistItems(QStringList() << path); - //TODO delete? - //m_tableView->setSortingEnabled(true); - if (item != m_historyPlaylistsItem) m_tableView->sortByColumn(0, Qt::AscendingOrder); + else + m_proxyModel->sort(-1); m_tableView->resizeColumnsToContents(); - QModelIndex index = m_proxyModel->index(0, 0); + index = m_proxyModel->index(0, 0); m_gridView->scrollToTop(); m_gridView->setCurrentIndex(index); m_tableView->setCurrentIndex(index); From 95ed844d8148ce12ac96231e0cefbb878e2465b3 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Wed, 19 Dec 2018 01:11:52 +0100 Subject: [PATCH 07/13] Qt: C89 changes and other cleanups. --- ui/drivers/qt/gridview.cpp | 18 ++++++++++++------ ui/drivers/qt/gridview.h | 2 +- ui/drivers/qt/playlist.cpp | 1 - ui/drivers/qt/thumbnailpackdownload.cpp | 1 - ui/drivers/qt/ui_qt_window.cpp | 9 +-------- ui/drivers/ui_qt.h | 1 - 6 files changed, 14 insertions(+), 18 deletions(-) diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp index e9e74c8128..59cd738840 100644 --- a/ui/drivers/qt/gridview.cpp +++ b/ui/drivers/qt/gridview.cpp @@ -27,17 +27,17 @@ void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &opt initStyleOption(&opt, index); - // draw the background + /* draw the background */ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, widget); - // draw the image + /* draw the image */ if (!pixmap.isNull()) { QPixmap pixmapScaled = pixmap.scaled(adjusted.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | Qt::AlignBottom, pixmapScaled); } - // draw the text + /* draw the text */ if (!opt.text.isEmpty()) { QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; @@ -195,6 +195,7 @@ void GridView::scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint) viewport()->update(); } +/* TODO: Make this more efficient by changing m_rectForRow for another data structure. Look at how Qt's own views do it. */ QModelIndex GridView::indexAt(const QPoint &point_) const { QPoint point(point_); @@ -298,6 +299,7 @@ void GridView::scrollContentsBy(int dx, int dy) emit(visibleItemsChangedMaybe()); } +/* TODO: Maybe add a way to get the previous/next visible indexes. */ QVector GridView::visibleIndexes() const { return m_visibleIndexes; } @@ -340,12 +342,16 @@ QRegion GridView::visualRegionForSelection(const QItemSelection &selection) cons { QRegion region; QItemSelectionRange range; + int i = 0; - foreach(range, selection) + for (i; i < selection.size(); i++) { - for (int row = range.top(); row <= range.bottom(); ++row) + range = selection.at(i); + int row = range.top(); + for (row; row <= range.bottom(); ++row) { - for (int column = range.left(); column < range.right(); ++column) + int column = range.left(); + for (column; column < range.right(); ++column) { QModelIndex index = model()->index(row, column, rootIndex()); region += visualRect(index); diff --git a/ui/drivers/qt/gridview.h b/ui/drivers/qt/gridview.h index 06537ec254..7e19d12aa1 100644 --- a/ui/drivers/qt/gridview.h +++ b/ui/drivers/qt/gridview.h @@ -71,4 +71,4 @@ private: mutable bool m_hashIsDirty; }; -#endif // GRIDVIEW_H +#endif diff --git a/ui/drivers/qt/playlist.cpp b/ui/drivers/qt/playlist.cpp index 7021c85d47..ce3742b413 100644 --- a/ui/drivers/qt/playlist.cpp +++ b/ui/drivers/qt/playlist.cpp @@ -1177,7 +1177,6 @@ void MainWindow::reloadPlaylists() QString MainWindow::getCurrentPlaylistPath() { QListWidgetItem *playlistItem = m_listWidget->currentItem(); - QHash contentHash; QString playlistPath; if (!playlistItem) diff --git a/ui/drivers/qt/thumbnailpackdownload.cpp b/ui/drivers/qt/thumbnailpackdownload.cpp index 48528ded06..8780e2f9d4 100644 --- a/ui/drivers/qt/thumbnailpackdownload.cpp +++ b/ui/drivers/qt/thumbnailpackdownload.cpp @@ -191,7 +191,6 @@ void MainWindow::onThumbnailPackDownloadFinished() reply->disconnect(); reply->close(); - //reply->deleteLater(); } void MainWindow::onThumbnailPackDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index c767a414d6..8203b7b587 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -247,7 +247,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) ,m_loadCoreWindow(new LoadCoreWindow(this)) ,m_timer(new QTimer(this)) - ,m_thumbnailTimer(new QTimer(this)) ,m_currentCore() ,m_currentCoreVersion() ,m_statusLabel(new QLabel(this)) @@ -322,6 +321,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_failedThumbnails(0) ,m_playlistThumbnailDownloadWasCanceled(false) ,m_pendingDirScrollPath() + ,m_thumbnailTimer(new QTimer(this)) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -338,8 +338,6 @@ MainWindow::MainWindow(QWidget *parent) : QLabel *gridProgressLabel = NULL; QHBoxLayout *gridFooterLayout = NULL; - //QApplication::setStyle(QStyleFactory::create("windowsvista")); - qRegisterMetaType >("ThumbnailWidget"); qRegisterMetaType("retro_task_callback_t"); @@ -1514,11 +1512,6 @@ QHash MainWindow::getCurrentContentHash() return getCurrentContentIndex().data(PlaylistModel::HASH).value >(); } -void MainWindow::onContentItemDoubleClicked(QTableWidgetItem*) -{ - onRunClicked(); -} - void MainWindow::onContentItemDoubleClicked(const QModelIndex &index) { Q_UNUSED(index); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 70eb1810e1..2c45b59906 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -422,7 +422,6 @@ private slots: void onSearchEnterPressed(); void onSearchLineEditEdited(const QString &text); void onContentItemDoubleClicked(const QModelIndex &index); - void onContentItemDoubleClicked(QTableWidgetItem *item); void onCoreLoadWindowClosed(); void onTreeViewItemsSelected(QModelIndexList selectedIndexes); void onSearchResetClicked(); From a396008bf6f50217c49237893c2d966f00ebb787 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Wed, 19 Dec 2018 08:57:00 +0100 Subject: [PATCH 08/13] Qt: More C89 changes. --- ui/drivers/qt/gridview.cpp | 3 ++- ui/drivers/qt/gridview.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp index 59cd738840..b1b788c0f2 100644 --- a/ui/drivers/qt/gridview.cpp +++ b/ui/drivers/qt/gridview.cpp @@ -346,8 +346,9 @@ QRegion GridView::visualRegionForSelection(const QItemSelection &selection) cons for (i; i < selection.size(); i++) { + int row; range = selection.at(i); - int row = range.top(); + row = range.top(); for (row; row <= range.bottom(); ++row) { int column = range.left(); diff --git a/ui/drivers/qt/gridview.h b/ui/drivers/qt/gridview.h index 7e19d12aa1..0aa2835845 100644 --- a/ui/drivers/qt/gridview.h +++ b/ui/drivers/qt/gridview.h @@ -44,7 +44,7 @@ protected slots: void rowsInserted(const QModelIndex &parent, int start, int end); void rowsRemoved(const QModelIndex &parent, int start, int end); void updateGeometries(); - void reset() override; + void reset(); protected: QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers); From 7bb23d15bd5c197d463a6aa5fcce717018f81ef8 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Wed, 19 Dec 2018 21:28:11 +0100 Subject: [PATCH 09/13] Fix last commit and a couple style changes. --- ui/drivers/qt/gridview.cpp | 13 ++++++------- ui/drivers/qt/ui_qt_load_core_window.cpp | 2 +- ui/drivers/qt/ui_qt_window.cpp | 3 ++- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp index b1b788c0f2..348c6a859b 100644 --- a/ui/drivers/qt/gridview.cpp +++ b/ui/drivers/qt/gridview.cpp @@ -342,17 +342,16 @@ QRegion GridView::visualRegionForSelection(const QItemSelection &selection) cons { QRegion region; QItemSelectionRange range; - int i = 0; + int i; - for (i; i < selection.size(); i++) + for (i = 0; i < selection.size(); i++) { - int row; range = selection.at(i); - row = range.top(); - for (row; row <= range.bottom(); ++row) + int row; + for (row = range.top(); row <= range.bottom(); row++) { - int column = range.left(); - for (column; column < range.right(); ++column) + int column; + for (column = range.left(); column < range.right(); column++) { QModelIndex index = model()->index(row, column, rootIndex()); region += visualRect(index); diff --git a/ui/drivers/qt/ui_qt_load_core_window.cpp b/ui/drivers/qt/ui_qt_load_core_window.cpp index 8179b82570..7cd344cd2b 100644 --- a/ui/drivers/qt/ui_qt_load_core_window.cpp +++ b/ui/drivers/qt/ui_qt_load_core_window.cpp @@ -31,7 +31,7 @@ extern "C" { #include #include #include -}; +} #define CORE_NAME_COLUMN 0 #define CORE_VERSION_COLUMN 1 diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 8203b7b587..60c519fc00 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -655,7 +655,8 @@ void MainWindow::updateVisibleItems() { if (m_viewType == VIEW_TYPE_ICONS) { QVector indexes = m_gridView->visibleIndexes(); - for (int i = 0; i < indexes.size(); i++) + int i; + for (i = 0; i < indexes.size(); i++) { m_playlistModel->loadThumbnail(m_proxyModel->mapToSource(indexes.at(i))); } From ca9952ceef36d43ce4f6544fe5985a4d8742ffb9 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Sun, 23 Dec 2018 00:45:00 +0100 Subject: [PATCH 10/13] Qt: make grid view customizable by stylesheet --- ui/drivers/qt/gridview.cpp | 188 +++++++++++++++++++++++---------- ui/drivers/qt/gridview.h | 21 +++- ui/drivers/qt/ui_qt_themes.h | 4 + ui/drivers/qt/ui_qt_window.cpp | 14 ++- ui/drivers/ui_qt.h | 22 ++++ 5 files changed, 188 insertions(+), 61 deletions(-) diff --git a/ui/drivers/qt/gridview.cpp b/ui/drivers/qt/gridview.cpp index 348c6a859b..9d53c8c506 100644 --- a/ui/drivers/qt/gridview.cpp +++ b/ui/drivers/qt/gridview.cpp @@ -6,21 +6,20 @@ /* http://www.informit.com/articles/article.aspx?p=1613548 */ -ThumbnailDelegate::ThumbnailDelegate(QObject* parent) : - QStyledItemDelegate(parent) +ThumbnailDelegate::ThumbnailDelegate(const GridItem &gridItem, QObject* parent) : + QStyledItemDelegate(parent), m_style(gridItem) { } - void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const { QStyleOptionViewItem opt = option; const QWidget *widget = opt.widget; QStyle *style = widget->style(); - int margin = 11; - int textMargin = 4; - int textHeight = painter->fontMetrics().height() + margin + margin; + int padding = m_style.padding; + int textTopMargin = 4; /* Qt seemingly reports -4 the actual line height. */ + int textHeight = painter->fontMetrics().height() + padding + padding; QRect rect = opt.rect; - QRect adjusted = rect.adjusted(margin, margin, -margin, -textHeight + textMargin); + QRect adjusted = rect.adjusted(padding, padding, -padding, -textHeight + textTopMargin); QPixmap pixmap = index.data(PlaylistModel::THUMBNAIL).value(); painter->save(); @@ -34,14 +33,14 @@ void ThumbnailDelegate::paint(QPainter* painter, const QStyleOptionViewItem &opt if (!pixmap.isNull()) { QPixmap pixmapScaled = pixmap.scaled(adjusted.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); - style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | Qt::AlignBottom, pixmapScaled); + style->drawItemPixmap(painter, adjusted, Qt::AlignHCenter | m_style.thumbnailVerticalAlignmentFlag, pixmapScaled); } /* draw the text */ if (!opt.text.isEmpty()) { QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; - QRect textRect = QRect(rect.x() + margin, rect.y() + adjusted.height() - textMargin + margin, rect.width() - 2 * margin, textHeight); + QRect textRect = QRect(rect.x() + padding, rect.y() + adjusted.height() - textTopMargin + padding, rect.width() - 2 * padding, textHeight); QString elidedText = painter->fontMetrics().elidedText(opt.text, opt.textElideMode, textRect.width(), Qt::TextShowMnemonic); if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) @@ -95,68 +94,67 @@ void GridView::calculateRectsIfNecessary() const const int maxWidth = viewport()->width(); - switch (m_viewMode) { - case Anchored: + switch (m_viewMode) { - int columns = (maxWidth - m_spacing) / (m_size + m_spacing); - if (columns > 0) + case Anchored: { - const int actualSpacing = (maxWidth - m_spacing - m_size - (columns - 1) * m_size) / columns; + int columns = (maxWidth - m_spacing) / (m_size + m_spacing); + if (columns > 0) + { + const int actualSpacing = (maxWidth - m_spacing - m_size - (columns - 1) * m_size) / columns; + for (row = 0; row < model()->rowCount(); ++row) + { + nextX = x + m_size + actualSpacing; + if (nextX > maxWidth) + { + x = m_spacing; + y += m_size + m_spacing; + nextX = x + m_size + actualSpacing; + } + m_rectForRow[row] = QRectF(x, y, m_size, m_size); + x = nextX; + } + } + break; + } + case Centered: + { + int columns = (maxWidth - m_spacing) / (m_size + m_spacing); + if (columns > 0) + { + const int actualSpacing = (maxWidth - columns * m_size) / (columns + 1); + x = actualSpacing; + for (row = 0; row < model()->rowCount(); ++row) + { + nextX = x + m_size + actualSpacing; + if (nextX > maxWidth) + { + x = actualSpacing; + y += m_size + m_spacing; + nextX = x + m_size + actualSpacing; + } + m_rectForRow[row] = QRectF(x, y, m_size, m_size); + x = nextX; + } + } + break; + } + case Simple: for (row = 0; row < model()->rowCount(); ++row) { - nextX = x + m_size + actualSpacing; + nextX = x + m_size + m_spacing; if (nextX > maxWidth) { x = m_spacing; y += m_size + m_spacing; - nextX = x + m_size + actualSpacing; + nextX = x + m_size + m_spacing; } m_rectForRow[row] = QRectF(x, y, m_size, m_size); x = nextX; } - m_idealHeight = y + m_size + m_spacing; - } - break; + break; } - case Centered: - { - int columns = (maxWidth - m_spacing) / (m_size + m_spacing); - if (columns > 0) - { - const int actualSpacing = (maxWidth - columns * m_size) / (columns + 1); - x = actualSpacing; - for (row = 0; row < model()->rowCount(); ++row) - { - nextX = x + m_size + actualSpacing; - if (nextX > maxWidth) - { - x = actualSpacing; - y += m_size + m_spacing; - nextX = x + m_size + actualSpacing; - } - m_rectForRow[row] = QRectF(x, y, m_size, m_size); - x = nextX; - } - m_idealHeight = y + m_size + m_spacing; - } - break; - } - case Simple: - for (row = 0; row < model()->rowCount(); ++row) - { - nextX = x + m_size + m_spacing; - if (nextX > maxWidth) - { - x = m_spacing; - y += m_size + m_spacing; - nextX = x + m_size + m_spacing; - } - m_rectForRow[row] = QRectF(x, y, m_size, m_size); - x = nextX; - } - break; - } - + m_idealHeight = y + m_size + m_spacing; m_hashIsDirty = false; viewport()->update(); } @@ -407,3 +405,77 @@ void GridView::updateGeometries() emit(visibleItemsChangedMaybe()); } + +QString GridView::getLayout() const +{ + switch (m_viewMode) + { + case Simple: + return "simple"; + case Anchored: + return "anchored"; + case Centered: + default: + return "centered"; + } +} + +void GridView::setLayout(QString layout) +{ + if (layout == "anchored") + m_viewMode = Anchored; + else if (layout == "centered") + m_viewMode = Centered; + else if (layout == "fixed") + m_viewMode = Simple; +} + +int GridView::getSpacing() const +{ + return m_spacing; +} + +void GridView::setSpacing(const int spacing) +{ + m_spacing = spacing; +} + +GridItem::GridItem(QWidget* parent) : QWidget(parent) +, thumbnailVerticalAlignmentFlag(Qt::AlignBottom) +, padding(11) +{ +} + +int GridItem::getPadding() const +{ + return padding; +} + +void GridItem::setPadding(const int value) +{ + padding = value; +} + +QString GridItem::getThumbnailVerticalAlign() const +{ + switch (thumbnailVerticalAlignmentFlag) + { + case Qt::AlignTop: + return "top"; + case Qt::AlignVCenter: + return "center"; + case Qt::AlignBottom: + default: + return "bottom"; + } +} + +void GridItem::setThumbnailVerticalAlign(const QString valign) +{ + if (valign == "top") + thumbnailVerticalAlignmentFlag = Qt::AlignTop; + else if (valign == "center") + thumbnailVerticalAlignmentFlag = Qt::AlignVCenter; + else if (valign == "bottom") + thumbnailVerticalAlignmentFlag = Qt::AlignBottom; +} diff --git a/ui/drivers/qt/gridview.h b/ui/drivers/qt/gridview.h index 0aa2835845..165781d91e 100644 --- a/ui/drivers/qt/gridview.h +++ b/ui/drivers/qt/gridview.h @@ -4,19 +4,32 @@ #include #include +#define DEFAULT_GRID_ITEM_MARGIN 11 +#define DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT "bottom" +#define DEFAULT_GRID_SPACING 7 +#define DEFAULT_GRID_LAYOUT "centered" + +class GridItem; + class ThumbnailDelegate : public QStyledItemDelegate { Q_OBJECT public: - ThumbnailDelegate(QObject* parent = 0); + ThumbnailDelegate(const GridItem &gridItem, QObject* parent = 0); void paint(QPainter* painter, const QStyleOptionViewItem &option, const QModelIndex& index) const; + +private: + const GridItem &m_style; }; class GridView : public QAbstractItemView { Q_OBJECT + Q_PROPERTY(QString layout READ getLayout WRITE setLayout DESIGNABLE true SCRIPTABLE true) + Q_PROPERTY(int spacing READ getSpacing WRITE setSpacing DESIGNABLE true SCRIPTABLE true) + public: enum ViewMode { @@ -35,6 +48,10 @@ public: void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint); void setGridSize(const int newSize); void setviewMode(ViewMode mode); + QString getLayout() const; + void setLayout(QString layout); + int getSpacing() const; + void setSpacing(const int spacing); signals: void visibleItemsChangedMaybe() const; @@ -63,7 +80,7 @@ private: void refresh(); int m_size = 255; - int m_spacing = 7; + int m_spacing = DEFAULT_GRID_SPACING; QVector m_visibleIndexes; ViewMode m_viewMode = Centered; mutable int m_idealHeight; diff --git a/ui/drivers/qt/ui_qt_themes.h b/ui/drivers/qt/ui_qt_themes.h index d4c3d327a9..7525730219 100644 --- a/ui/drivers/qt/ui_qt_themes.h +++ b/ui/drivers/qt/ui_qt_themes.h @@ -431,5 +431,9 @@ static const QString qt_theme_dark_stylesheet = QStringLiteral(R"( GridView { background-color:rgb(25,25,25); selection-color: white; + qproperty-layout: "fixed"; + } + GridItem { + qproperty-thumbnailvalign: "center"; } )"); diff --git a/ui/drivers/qt/ui_qt_window.cpp b/ui/drivers/qt/ui_qt_window.cpp index 60c519fc00..98bab15165 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -322,6 +322,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_playlistThumbnailDownloadWasCanceled(false) ,m_pendingDirScrollPath() ,m_thumbnailTimer(new QTimer(this)) + ,m_gridItem(this) { settings_t *settings = config_get_ptr(); QDir playlistDir(settings->paths.directory_playlist); @@ -417,7 +418,7 @@ MainWindow::MainWindow(QWidget *parent) : m_tableView->setSortingEnabled(true); m_tableView->verticalHeader()->setVisible(false); - m_gridView->setItemDelegate(new ThumbnailDelegate(this)); + m_gridView->setItemDelegate(new ThumbnailDelegate(m_gridItem, this)); m_gridView->setModel(m_proxyModel); m_logWidget->setObjectName("logWidget"); @@ -566,6 +567,7 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_thumbnailTimer, SIGNAL(timeout()), this, SLOT(updateVisibleItems())); connect(this, SIGNAL(updateThumbnails()), this, SLOT(updateVisibleItems())); + /* TODO: Handle scroll and resize differently. */ connect(m_gridView, SIGNAL(visibleItemsChangedMaybe()), this, SLOT(startTimer())); connect(m_gridView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(currentItemChanged(const QModelIndex&))); @@ -1134,6 +1136,8 @@ void MainWindow::setTheme(Theme theme) { m_currentTheme = theme; + setDefaultCustomProperties(); + switch(theme) { case THEME_SYSTEM_DEFAULT: @@ -1159,6 +1163,14 @@ void MainWindow::setTheme(Theme theme) } } +void MainWindow::setDefaultCustomProperties() +{ + m_gridView->setLayout(QString(DEFAULT_GRID_LAYOUT)); + m_gridView->setSpacing(DEFAULT_GRID_SPACING); + m_gridItem.setThumbnailVerticalAlign(QString(DEFAULT_GRID_ITEM_THUMBNAIL_ALIGNMENT)); + m_gridItem.setPadding(DEFAULT_GRID_ITEM_MARGIN); +} + void MainWindow::changeThumbnailType(ThumbnailType type) { m_playlistModel->setThumbnailType(type); diff --git a/ui/drivers/ui_qt.h b/ui/drivers/ui_qt.h index 2c45b59906..b0fe0b30b6 100644 --- a/ui/drivers/ui_qt.h +++ b/ui/drivers/ui_qt.h @@ -271,6 +271,26 @@ public slots: void appendMessage(const QString& text); }; +/* Used to store styling since delegates don't inherit QWidget. */ +class GridItem : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QString thumbnailvalign READ getThumbnailVerticalAlign WRITE setThumbnailVerticalAlign) + Q_PROPERTY(int padding READ getPadding WRITE setPadding) + +public: + GridItem(QWidget* parent); + + Qt::AlignmentFlag thumbnailVerticalAlignmentFlag; + int padding; + + int getPadding() const; + void setPadding(const int value); + QString getThumbnailVerticalAlign() const; + void setThumbnailVerticalAlign(const QString valign); +}; + class MainWindow : public QMainWindow { Q_OBJECT @@ -347,6 +367,7 @@ public: QString getSpecialPlaylistPath(SpecialPlaylist playlist); QVector > getPlaylists(); QString getScrubbedString(QString str); + void setDefaultCustomProperties(); signals: void thumbnailChanged(const QPixmap &pixmap); @@ -574,6 +595,7 @@ private: QString m_pendingDirScrollPath; QTimer *m_thumbnailTimer; + GridItem m_gridItem; protected: void closeEvent(QCloseEvent *event); From e0c01d658d5d1f3ba2f13771e1261119246d1445 Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Sun, 23 Dec 2018 00:46:44 +0100 Subject: [PATCH 11/13] Revert "Qt: Implement custom playlist model and grid view." This reverts commit 240856f19c0adcb77af8f6ac7e6aea41f8e458fc. --- intl/msg_hash_us.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 040d8ae696..41f76c5d83 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -7821,6 +7821,8 @@ MSG_HASH( MSG_HASH( MSG_MISSING_ASSETS, "Warning: Missing assets, use the Online Updater if available" -) -MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SAVE_POSITION, - "Remember Window Position and Size") + ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_WINDOW_SAVE_POSITION, + "Remember Window Position and Size" + ) From e484f30af6f602614a33b5c554abfc60ff9a09ce Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Sun, 23 Dec 2018 01:10:04 +0100 Subject: [PATCH 12/13] revert commit e981afcca8 --- pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj index bb74c23f60..f228bad8bf 100644 --- a/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj +++ b/pkg/msvc/msvc-2017/RetroArch-msvc2017.vcxproj @@ -281,7 +281,7 @@ true - $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ + $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -290,7 +290,7 @@ true - $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ + $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(CG_LIB64_PATH);$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -349,7 +349,7 @@ false - $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ + $(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -358,7 +358,7 @@ false - $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\QtNetwork;$(QtDirectory)\msvc2017_64\include\QtConcurrent;$(QtDirectory)\msvc2017_64\include\ + $(CG_INC_PATH);$(IncludePath);$(DXSDK_DIR)Include;$(QtDirectory)\msvc2017_64\include\QtCore;$(QtDirectory)\msvc2017_64\include\QtWidgets;$(QtDirectory)\msvc2017_64\include\QtGui;$(QtDirectory)\msvc2017_64\include\ $(DXSDK_DIR)Lib\x64;$(CG_LIB64_PATH);$(QtDirectory)\msvc2017_64\lib;$(LibraryPath) AllRules.ruleset @@ -423,7 +423,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -444,7 +444,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -506,7 +506,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -527,7 +527,7 @@ Console true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -604,7 +604,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -630,7 +630,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB_PATH) @@ -707,7 +707,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -733,7 +733,7 @@ true true true - msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;Qt5Network.lib;Qt5Concurrent.lib;%(AdditionalDependencies) + msimg32.lib;winmm.lib;Dinput8.lib;dxguid.lib;Iphlpapi.lib;qtmain.lib;Qt5Widgets.lib;Qt5Gui.lib;Qt5Core.lib;%(AdditionalDependencies) $(CG_LIB64_PATH) @@ -759,13 +759,49 @@ CompileAsC CompileAsC - + /bigobj %(AdditionalOptions) /bigobj %(AdditionalOptions) /bigobj %(AdditionalOptions) /bigobj %(AdditionalOptions) + + true + true + false + false + true + false + false + true + true + true + false + false + true + false + false + true + + + true + true + false + false + true + false + false + true + true + true + false + false + true + false + false + true + @@ -872,4 +908,4 @@ - \ No newline at end of file + From ab9c235cfa67b3118f3623628d50d928192b640f Mon Sep 17 00:00:00 2001 From: CozmoP <25121396+CozmoP@users.noreply.github.com> Date: Sun, 23 Dec 2018 02:59:42 +0100 Subject: [PATCH 13/13] Qt: Initialize m_thumbnailType. --- 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 98bab15165..7444dd7499 100644 --- a/ui/drivers/qt/ui_qt_window.cpp +++ b/ui/drivers/qt/ui_qt_window.cpp @@ -291,6 +291,7 @@ MainWindow::MainWindow(QWidget *parent) : ,m_zoomSlider(NULL) ,m_lastZoomSliderValue(0) ,m_viewType(VIEW_TYPE_LIST) + ,m_thumbnailType(THUMBNAIL_TYPE_BOXART) ,m_gridProgressBar(NULL) ,m_gridProgressWidget(NULL) ,m_currentGridHash()