From 88874f0dcd848ef97bd8fcd41c16ed441ab5ba66 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 30 Jan 2021 21:53:53 +0100 Subject: [PATCH] Remove NewClosure --- ext/libstrawberry-common/CMakeLists.txt | 2 - ext/libstrawberry-common/core/closure.cpp | 85 ------- ext/libstrawberry-common/core/closure.h | 236 ------------------ src/collection/collectionmodel.cpp | 14 +- src/collection/collectionmodel.h | 2 +- src/device/deviceproperties.cpp | 14 +- src/device/deviceproperties.h | 2 +- src/dialogs/edittagdialog.cpp | 19 +- src/dialogs/edittagdialog.h | 3 +- src/engine/gstengine.cpp | 14 +- src/engine/gstengine.h | 2 +- src/moodbar/moodbaritemdelegate.cpp | 24 +- src/moodbar/moodbaritemdelegate.h | 5 +- src/organize/organizedialog.cpp | 10 +- src/playlist/playlist.cpp | 25 +- src/playlist/playlist.h | 2 +- src/playlist/playlistdelegates.cpp | 12 +- src/playlist/playlistdelegates.h | 3 +- src/playlist/playlistmanager.cpp | 17 +- src/playlist/playlistmanager.h | 2 +- src/qobuz/qobuzrequest.cpp | 2 +- .../playlistgeneratorinserter.cpp | 13 +- .../playlistgeneratorinserter.h | 3 +- .../smartplaylistsearchpreview.cpp | 14 +- .../smartplaylistsearchpreview.h | 3 +- tests/CMakeLists.txt | 1 - tests/src/closure_test.cpp | 149 ----------- 27 files changed, 133 insertions(+), 545 deletions(-) delete mode 100644 ext/libstrawberry-common/core/closure.cpp delete mode 100644 ext/libstrawberry-common/core/closure.h delete mode 100644 tests/src/closure_test.cpp diff --git a/ext/libstrawberry-common/CMakeLists.txt b/ext/libstrawberry-common/CMakeLists.txt index ae92d1df4..0630f7889 100644 --- a/ext/libstrawberry-common/CMakeLists.txt +++ b/ext/libstrawberry-common/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.0) set(SOURCES - core/closure.cpp core/logging.cpp core/messagehandler.cpp core/messagereply.cpp @@ -9,7 +8,6 @@ set(SOURCES ) set(HEADERS - core/closure.h core/messagehandler.h core/messagereply.h core/workerpool.h diff --git a/ext/libstrawberry-common/core/closure.cpp b/ext/libstrawberry-common/core/closure.cpp deleted file mode 100644 index 7bf1270a4..000000000 --- a/ext/libstrawberry-common/core/closure.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* This file is part of Strawberry. - Copyright 2011, David Sansome - - Strawberry is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Strawberry is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Strawberry. If not, see . -*/ - -#include -#include -#include -#include -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) -# include -#endif - -#include "closure.h" - -#include "core/timeconstants.h" - -namespace _detail { - -ClosureBase::ClosureBase(ObjectHelper *helper) : helper_(helper) {} - -ClosureBase::~ClosureBase() {} - -CallbackClosure::CallbackClosure(QObject *sender, const char *signal, std::function callback) - : ClosureBase(new ObjectHelper(sender, signal, this)), - callback_(callback) { -} - -void CallbackClosure::Invoke() { - callback_(); -} - -ObjectHelper* ClosureBase::helper() const { - return helper_; -} - -ObjectHelper::ObjectHelper(QObject *sender, const char *signal, ClosureBase *closure) : closure_(closure) { - - QObject::connect(sender, signal, SLOT(Invoked())); - QObject::connect(sender, &QObject::destroyed, this, &ObjectHelper::deleteLater); - -} - -ObjectHelper::~ObjectHelper() {} - -void ObjectHelper::Invoked() { - closure_->Invoke(); - deleteLater(); -} - -void Unpack(QList*) {} - -} // namespace _detail - -_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::function callback) { - return new _detail::CallbackClosure(sender, signal, callback); -} - -void DoAfter(QObject *receiver, const char *slot, int msec) { - QTimer::singleShot(msec, receiver, slot); -} - -void DoInAMinuteOrSo(QObject *receiver, const char *slot) { - -#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) - int msec = (60 + QRandomGenerator::global()->bounded(1, 60)) * kMsecPerSec; -#else - int msec = (60 + (qrand() % 60)) * kMsecPerSec; -#endif - - DoAfter(receiver, slot, msec); - -} diff --git a/ext/libstrawberry-common/core/closure.h b/ext/libstrawberry-common/core/closure.h deleted file mode 100644 index 3888885c7..000000000 --- a/ext/libstrawberry-common/core/closure.h +++ /dev/null @@ -1,236 +0,0 @@ -/* This file is part of Strawberry. - Copyright 2011, David Sansome - - Strawberry is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Strawberry is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Strawberry. If not, see . -*/ - -#ifndef CLOSURE_H -#define CLOSURE_H - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core/logging.h" - -namespace _detail { - -class ObjectHelper; - -// Interface for ObjectHelper to call on signal emission. -class ClosureBase { - public: - virtual ~ClosureBase(); - virtual void Invoke() = 0; - - // Tests only. - ObjectHelper *helper() const; - - protected: - explicit ClosureBase(ObjectHelper*); - ObjectHelper *helper_; - - private: - Q_DISABLE_COPY(ClosureBase) - -}; - -// QObject helper as templated QObjects do not work. -// Connects to the given signal and invokes the closure when called. -// Deletes itself and the Closure after being invoked. -class ObjectHelper : public QObject { - Q_OBJECT - public: - ObjectHelper(QObject *sender, const char *signal, ClosureBase *closure); - ~ObjectHelper() override; - - private slots: - void Invoked(); - - private: - std::unique_ptr closure_; - Q_DISABLE_COPY(ObjectHelper) - -}; - -// Helpers for unpacking a variadic template list. - -// Base case of no arguments. -void Unpack(QList*); - -template -void Unpack(QList *list, const Arg &arg) { - list->append(Q_ARG(Arg, arg)); -} - -template -void Unpack(QList *list, const Head &head, const Tail&... tail) { - Unpack(list, head); - Unpack(list, tail...); -} - -template -class Closure : public ClosureBase { - public: - Closure( - QObject *sender, - const char *signal, - QObject *receiver, - const char *slot, - const Args&... args) - : ClosureBase(new ObjectHelper(sender, signal, this)), - // std::bind is the easiest way to store an argument list. - function_(std::bind(&Closure::Call, this, args...)), - receiver_(receiver) { - const QMetaObject *meta_receiver = receiver->metaObject(); - QByteArray normalised_slot = QMetaObject::normalizedSignature(slot + 1); - const int index = meta_receiver->indexOfSlot(normalised_slot.constData()); - Q_ASSERT(index != -1); - slot_ = meta_receiver->method(index); - QObject::connect(receiver_, &QObject::destroyed, helper_, &QObject::deleteLater); - } - - void Invoke() override { - function_(); - } - - private: - void Call(const Args&... args) { - QList arg_list; - Unpack(&arg_list, args...); - - slot_.invoke( - receiver_, - arg_list.size() > 0 ? arg_list[0] : QGenericArgument(), - arg_list.size() > 1 ? arg_list[1] : QGenericArgument(), - arg_list.size() > 2 ? arg_list[2] : QGenericArgument(), - arg_list.size() > 3 ? arg_list[3] : QGenericArgument(), - arg_list.size() > 4 ? arg_list[4] : QGenericArgument(), - arg_list.size() > 5 ? arg_list[5] : QGenericArgument(), - arg_list.size() > 6 ? arg_list[6] : QGenericArgument(), - arg_list.size() > 7 ? arg_list[7] : QGenericArgument(), - arg_list.size() > 8 ? arg_list[8] : QGenericArgument(), - arg_list.size() > 9 ? arg_list[9] : QGenericArgument()); - } - - std::function function_; - QObject *receiver_; - QMetaMethod slot_; -}; - -template -class SharedClosure : public Closure { - public: - SharedClosure( - QSharedPointer sender, - const char *signal, - QObject *receiver, - const char *slot, - const Args&... args) - : Closure( - sender.data(), - signal, - receiver, - slot, - args...), - data_(sender) { - } - - private: - QSharedPointer data_; -}; - -class CallbackClosure : public ClosureBase { - public: - CallbackClosure(QObject *sender, const char *signal, std::function callback); - - void Invoke() override; - - private: - std::function callback_; -}; - -} // namespace _detail - -template -_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, QObject *receiver, const char *slot, const Args&... args) { - return new _detail::Closure(sender, signal, receiver, slot, args...); -} - -// QSharedPointer variant -template -_detail::ClosureBase *NewClosure(QSharedPointer sender, const char *signal, QObject *receiver, const char *slot, const Args&... args) { - return new _detail::SharedClosure(sender, signal, receiver, slot, args...); -} - -_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::function callback); - -template -_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, std::function callback, const Args&... args) { - return NewClosure(sender, signal, std::bind(callback, args...)); -} - -template -_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, void (*callback)(Args...), const Args&... args) { - return NewClosure(sender, signal, std::bind(callback, args...)); -} - -template -_detail::ClosureBase *NewClosure(QObject *sender, const char *signal, T *receiver, Unused (T::*callback)(Args...), const Args&... args) { - return NewClosure(sender, signal, std::bind(callback, receiver, args...)); -} - -template -_detail::ClosureBase *NewClosure(QFuture future, QObject *receiver, const char *slot, const Args&... args) { - QFutureWatcher *watcher = new QFutureWatcher; - watcher->setFuture(future); - QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater); - return NewClosure(watcher, SIGNAL(finished()), receiver, slot, args...); -} - -template -_detail::ClosureBase *NewClosure(QFuture future, const F &callback, const Args&... args) { - QFutureWatcher *watcher = new QFutureWatcher; - watcher->setFuture(future); - QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater); - return NewClosure(watcher, SIGNAL(finished()), callback, args...); -} - -void DoAfter(QObject *receiver, const char *slot, int msec); -void DoAfter(std::function callback, std::chrono::milliseconds msec); -void DoInAMinuteOrSo(QObject *receiver, const char *slot); - -template -void DoAfter(std::function callback, std::chrono::duration duration) { - QTimer *timer = new QTimer; - timer->setSingleShot(true); - QObject::connect(timer, &QTimer::timeout, callback); - QObject::connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater); - std::chrono::milliseconds msec = std::chrono::duration_cast(duration); - timer->start(msec.count()); -} - -#endif // CLOSURE_H diff --git a/src/collection/collectionmodel.cpp b/src/collection/collectionmodel.cpp index d3fe71429..49063809e 100644 --- a/src/collection/collectionmodel.cpp +++ b/src/collection/collectionmodel.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,6 @@ #include #include "core/application.h" -#include "core/closure.h" #include "core/database.h" #include "core/iconloader.h" #include "core/logging.h" @@ -915,18 +915,22 @@ void CollectionModel::ResetAsync() { #else QFuture future = QtConcurrent::run(this, &CollectionModel::RunQuery, root_); #endif - NewClosure(future, this, SLOT(ResetAsyncQueryFinished(QFuture)), future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, &CollectionModel::ResetAsyncQueryFinished); } -void CollectionModel::ResetAsyncQueryFinished(QFuture future) { +void CollectionModel::ResetAsyncQueryFinished() { + + QFutureWatcher *watcher = static_cast*>(sender()); + const struct QueryResult result = watcher->result(); + watcher->deleteLater(); if (QThread::currentThread() != thread() && QThread::currentThread() != backend_->thread()) { backend_->Close(); } - const struct QueryResult result = future.result(); - BeginReset(); root_->lazy_loaded = true; diff --git a/src/collection/collectionmodel.h b/src/collection/collectionmodel.h index 4879faa17..6b5924a12 100644 --- a/src/collection/collectionmodel.h +++ b/src/collection/collectionmodel.h @@ -228,7 +228,7 @@ class CollectionModel : public SimpleTreeModel { void ClearDiskCache(); // Called after ResetAsync - void ResetAsyncQueryFinished(QFuture future); + void ResetAsyncQueryFinished(); void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result); diff --git a/src/device/deviceproperties.cpp b/src/device/deviceproperties.cpp index ea7b50eb7..88d0bdbfe 100644 --- a/src/device/deviceproperties.cpp +++ b/src/device/deviceproperties.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +48,6 @@ #include #include -#include "core/closure.h" #include "core/iconloader.h" #include "core/musicstorage.h" #include "widgets/freespacebar.h" @@ -247,7 +247,9 @@ void DeviceProperties::UpdateFormats() { #else QFuture future = QtConcurrent::run(std::bind(&ConnectedDevice::GetSupportedFiletypes, device, &supported_formats_)); #endif - NewClosure(future, this, SLOT(UpdateFormatsFinished(QFuture)), future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, &DeviceProperties::UpdateFormatsFinished); ui_->formats_stack->setCurrentWidget(ui_->formats_page_loading); updating_formats_ = true; @@ -283,11 +285,15 @@ void DeviceProperties::accept() { void DeviceProperties::OpenDevice() { manager_->Connect(index_); } -void DeviceProperties::UpdateFormatsFinished(QFuture future) { +void DeviceProperties::UpdateFormatsFinished() { + + QFutureWatcher *watcher = static_cast*>(sender()); + bool result = watcher->result(); + watcher->deleteLater(); updating_formats_ = false; - if (!future.result()) { + if (!result) { supported_formats_.clear(); } diff --git a/src/device/deviceproperties.h b/src/device/deviceproperties.h index 3361b0201..0d6bad982 100644 --- a/src/device/deviceproperties.h +++ b/src/device/deviceproperties.h @@ -59,7 +59,7 @@ class DeviceProperties : public QDialog { private slots: void ModelChanged(); void OpenDevice(); - void UpdateFormatsFinished(QFuture future); + void UpdateFormatsFinished(); private: Ui_DeviceProperties *ui_; diff --git a/src/dialogs/edittagdialog.cpp b/src/dialogs/edittagdialog.cpp index d8426aa70..5a1d87acd 100644 --- a/src/dialogs/edittagdialog.cpp +++ b/src/dialogs/edittagdialog.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,6 @@ #include #include "core/application.h" -#include "core/closure.h" #include "core/iconloader.h" #include "core/logging.h" #include "core/tagreaderclient.h" @@ -297,15 +297,24 @@ void EditTagDialog::SetSongs(const SongList &s, const PlaylistItemList &items) { #else QFuture> future = QtConcurrent::run(this, &EditTagDialog::LoadData, s); #endif - NewClosure(future, this, SLOT(SetSongsFinished(QFuture>)), future); + QFutureWatcher> *watcher = new QFutureWatcher>(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher>::finished, this, &EditTagDialog::SetSongsFinished); } -void EditTagDialog::SetSongsFinished(QFuture> future) { +void EditTagDialog::SetSongsFinished() { - if (!SetLoading(QString())) return; + QFutureWatcher> *watcher = static_cast>*>(sender()); + QList result_data = watcher->result(); + watcher->deleteLater(); + + if (!SetLoading(QString())) { + return; + } + + data_ = result_data; - data_ = future.result(); if (data_.count() == 0) { // If there were no valid songs, disable everything ui_->song_list->setEnabled(false); diff --git a/src/dialogs/edittagdialog.h b/src/dialogs/edittagdialog.h index 02f483690..d3e66c646 100644 --- a/src/dialogs/edittagdialog.h +++ b/src/dialogs/edittagdialog.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -101,7 +100,7 @@ class EditTagDialog : public QDialog { }; private slots: - void SetSongsFinished(QFuture> future); + void SetSongsFinished(); void AcceptFinished(); void SelectionChanged(); diff --git a/src/engine/gstengine.cpp b/src/engine/gstengine.cpp index 198ff8975..19bccefc5 100644 --- a/src/engine/gstengine.cpp +++ b/src/engine/gstengine.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -50,7 +51,6 @@ #include #include -#include "core/closure.h" #include "core/logging.h" #include "core/taskmanager.h" #include "core/timeconstants.h" @@ -242,7 +242,13 @@ bool GstEngine::Play(const quint64 offset_nanosec) { if (!current_pipeline_ || current_pipeline_->is_buffering()) return false; QFuture future = current_pipeline_->SetState(GST_STATE_PLAYING); - NewClosure(future, this, SLOT(PlayDone(QFuture, quint64, int)), future, offset_nanosec, current_pipeline_->id()); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + int pipeline_id = current_pipeline_->id(); + QObject::connect(watcher, &QFutureWatcher::finished, this, [this, watcher, offset_nanosec, pipeline_id]() { + PlayDone(watcher->result(), offset_nanosec, pipeline_id); + watcher->deleteLater(); + }); if (is_fading_out_to_pause_) { current_pipeline_->SetState(GST_STATE_PAUSED); @@ -634,9 +640,7 @@ void GstEngine::SeekNow() { } } -void GstEngine::PlayDone(QFuture future, const quint64 offset_nanosec, const int pipeline_id) { - - GstStateChangeReturn ret = future.result(); +void GstEngine::PlayDone(const GstStateChangeReturn ret, const quint64 offset_nanosec, const int pipeline_id) { if (!current_pipeline_ || pipeline_id != current_pipeline_->id()) { return; diff --git a/src/engine/gstengine.h b/src/engine/gstengine.h index 9d4a8cf3b..da139d10a 100644 --- a/src/engine/gstengine.h +++ b/src/engine/gstengine.h @@ -120,7 +120,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer { void FadeoutFinished(); void FadeoutPauseFinished(); void SeekNow(); - void PlayDone(QFuture future, const quint64, const int); + void PlayDone(const GstStateChangeReturn ret, const quint64, const int); void BufferingStarted(); void BufferingProgress(int percent); diff --git a/src/moodbar/moodbaritemdelegate.cpp b/src/moodbar/moodbaritemdelegate.cpp index f2db69c56..1a537a247 100644 --- a/src/moodbar/moodbaritemdelegate.cpp +++ b/src/moodbar/moodbaritemdelegate.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,6 @@ #include #include "core/application.h" -#include "core/closure.h" #include "playlist/playlist.h" #include "playlist/playlistview.h" @@ -212,11 +212,16 @@ void MoodbarItemDelegate::StartLoadingColors(const QUrl &url, const QByteArray & data->state_ = Data::State_LoadingColors; QFuture future = QtConcurrent::run(MoodbarRenderer::Colors, bytes, style_, qApp->palette()); - NewClosure(future, this, SLOT(ColorsLoaded(QUrl, QFuture)), url, future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, [this, watcher, url]() { + ColorsLoaded(url, watcher->result()); + watcher->deleteLater(); + }); } -void MoodbarItemDelegate::ColorsLoaded(const QUrl &url, QFuture future) { +void MoodbarItemDelegate::ColorsLoaded(const QUrl &url, const ColorVector &colors) { Data *data = data_[url]; if (!data) { @@ -227,7 +232,7 @@ void MoodbarItemDelegate::ColorsLoaded(const QUrl &url, QFuture fut return; } - data->colors_ = future.result(); + data->colors_ = colors; // Load the image next. StartLoadingImage(url, data); @@ -239,11 +244,16 @@ void MoodbarItemDelegate::StartLoadingImage(const QUrl &url, Data *data) { data->state_ = Data::State_LoadingImage; QFuture future = QtConcurrent::run(MoodbarRenderer::RenderToImage, data->colors_, data->desired_size_); - NewClosure(future, this, SLOT(ImageLoaded(QUrl, QFuture)), url, future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, [this, watcher, url]() { + ImageLoaded(url, watcher->result()); + watcher->deleteLater(); + }); } -void MoodbarItemDelegate::ImageLoaded(const QUrl &url, QFuture future) { +void MoodbarItemDelegate::ImageLoaded(const QUrl &url, const QImage &image) { Data *data = data_[url]; if (!data) { @@ -254,8 +264,6 @@ void MoodbarItemDelegate::ImageLoaded(const QUrl &url, QFuture future) { return; } - QImage image(future.result()); - // If the desired size changed then don't even bother converting the image // to a pixmap, just reload it at the new size. if (!image.isNull() && data->desired_size_ != image.size()) { diff --git a/src/moodbar/moodbaritemdelegate.h b/src/moodbar/moodbaritemdelegate.h index f4e41c4e2..b1142794e 100644 --- a/src/moodbar/moodbaritemdelegate.h +++ b/src/moodbar/moodbaritemdelegate.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -52,8 +51,8 @@ class MoodbarItemDelegate : public QItemDelegate { void ReloadSettings(); void DataLoaded(const QUrl &url, MoodbarPipeline *pipeline); - void ColorsLoaded(const QUrl &url, QFuture future); - void ImageLoaded(const QUrl &url, QFuture future); + void ColorsLoaded(const QUrl &url, const ColorVector &colors); + void ImageLoaded(const QUrl &url, const QImage &image); private: struct Data { diff --git a/src/organize/organizedialog.cpp b/src/organize/organizedialog.cpp index ff805f1ba..b196097fa 100644 --- a/src/organize/organizedialog.cpp +++ b/src/organize/organizedialog.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -56,7 +58,6 @@ #include #include -#include "core/closure.h" #include "core/iconloader.h" #include "core/musicstorage.h" #include "core/tagreaderclient.h" @@ -394,7 +395,12 @@ bool OrganizeDialog::SetFilenames(const QStringList &filenames) { #else songs_future_ = QtConcurrent::run(this, &OrganizeDialog::LoadSongsBlocking, filenames); #endif - NewClosure(songs_future_, [=]() { SetSongs(songs_future_.result()); }); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(songs_future_); + QObject::connect(watcher, &QFutureWatcher::finished, this, [=]() { + SetSongs(watcher->result()); + watcher->deleteLater(); + }); SetLoadingSongs(true); return true; diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 482f7d152..e3bbc5eed 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,6 @@ #include #include "core/application.h" -#include "core/closure.h" #include "core/logging.h" #include "core/mimedata.h" #include "core/tagreaderclient.h" @@ -406,7 +406,12 @@ void Playlist::SongSaveComplete(TagReaderReply *reply, const QPersistentModelInd PlaylistItemPtr item = item_at(idx.row()); if (item) { QFuture future = item->BackgroundReload(); - NewClosure(future, this, SLOT(ItemReloadComplete(QPersistentModelIndex)), idx); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, [this, watcher, idx]() { + ItemReloadComplete(idx); + watcher->deleteLater(); + }); } } else { @@ -1407,20 +1412,24 @@ void Playlist::Restore() { cancel_restore_ = false; #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QFuture> future = QtConcurrent::run(&PlaylistBackend::GetPlaylistItems, backend_, id_); + QFuture future = QtConcurrent::run(&PlaylistBackend::GetPlaylistItems, backend_, id_); #else - QFuture> future = QtConcurrent::run(backend_, &PlaylistBackend::GetPlaylistItems, id_); + QFuture future = QtConcurrent::run(backend_, &PlaylistBackend::GetPlaylistItems, id_); #endif - NewClosure(future, this, SLOT(ItemsLoaded(QFuture)), future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, &Playlist::ItemsLoaded); } -void Playlist::ItemsLoaded(QFuture future) { +void Playlist::ItemsLoaded() { + + QFutureWatcher *watcher = static_cast*>(sender()); + PlaylistItemList items = watcher->result(); + watcher->deleteLater(); if (cancel_restore_) return; - PlaylistItemList items = future.result(); - // Backend returns empty elements for collection items which it couldn't match (because they got deleted); we don't need those QMutableListIterator it(items); while (it.hasNext()) { diff --git a/src/playlist/playlist.h b/src/playlist/playlist.h index 526b2db2a..dfc51e652 100644 --- a/src/playlist/playlist.h +++ b/src/playlist/playlist.h @@ -377,7 +377,7 @@ class Playlist : public QAbstractListModel { void QueueLayoutChanged(); void SongSaveComplete(TagReaderReply *reply, const QPersistentModelIndex &idx); void ItemReloadComplete(const QPersistentModelIndex &idx); - void ItemsLoaded(QFuture future); + void ItemsLoaded(); void SongInsertVetoListenerDestroyed(); private: diff --git a/src/playlist/playlistdelegates.cpp b/src/playlist/playlistdelegates.cpp index 08ea540fe..019bb610f 100644 --- a/src/playlist/playlistdelegates.cpp +++ b/src/playlist/playlistdelegates.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -62,7 +63,6 @@ #include #include -#include "core/closure.h" #include "core/iconloader.h" #include "core/logging.h" #include "core/player.h" @@ -405,7 +405,9 @@ static TagCompletionModel *InitCompletionModel(CollectionBackend *backend, Playl TagCompleter::TagCompleter(CollectionBackend *backend, Playlist::Column column, QLineEdit *editor) : QCompleter(editor), editor_(editor) { QFuture future = QtConcurrent::run(&InitCompletionModel, backend, column); - NewClosure(future, this, SLOT(ModelReady(QFuture)), future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, &TagCompleter::ModelReady); } @@ -413,9 +415,11 @@ TagCompleter::~TagCompleter() { delete model(); } -void TagCompleter::ModelReady(QFuture future) { +void TagCompleter::ModelReady() { - TagCompletionModel *model = future.result(); + QFutureWatcher *watcher = static_cast*>(sender()); + TagCompletionModel *model = watcher->result(); + watcher->deleteLater(); setModel(model); setCaseSensitivity(Qt::CaseInsensitive); editor_->setCompleter(this); diff --git a/src/playlist/playlistdelegates.h b/src/playlist/playlistdelegates.h index b81475437..559b86447 100644 --- a/src/playlist/playlistdelegates.h +++ b/src/playlist/playlistdelegates.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -151,7 +150,7 @@ class TagCompleter : public QCompleter { ~TagCompleter() override; private slots: - void ModelReady(QFuture future); + void ModelReady(); private: QLineEdit *editor_; diff --git a/src/playlist/playlistmanager.cpp b/src/playlist/playlistmanager.cpp index dcee9ed63..43fca4dc1 100644 --- a/src/playlist/playlistmanager.cpp +++ b/src/playlist/playlistmanager.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,6 @@ #include #include "core/application.h" -#include "core/closure.h" #include "core/logging.h" #include "core/player.h" #include "core/utilities.h" @@ -220,18 +220,23 @@ void PlaylistManager::Save(const int id, const QString &filename, const Playlist else { // Playlist is not in the playlist manager: probably save action was triggered from the left side bar and the playlist isn't loaded. #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) - QFuture> future = QtConcurrent::run(&PlaylistBackend::GetPlaylistSongs, playlist_backend_, id); + QFuture future = QtConcurrent::run(&PlaylistBackend::GetPlaylistSongs, playlist_backend_, id); #else - QFuture> future = QtConcurrent::run(playlist_backend_, &PlaylistBackend::GetPlaylistSongs, id); + QFuture future = QtConcurrent::run(playlist_backend_, &PlaylistBackend::GetPlaylistSongs, id); #endif - NewClosure(future, this, SLOT(ItemsLoadedForSavePlaylist(QFuture, QString, Playlist::Path)), future, filename, path_type); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, [this, watcher, filename, path_type]() { + ItemsLoadedForSavePlaylist(watcher->result(), filename, path_type); + watcher->deleteLater(); + }); } } -void PlaylistManager::ItemsLoadedForSavePlaylist(QFuture future, const QString &filename, const Playlist::Path path_type) { +void PlaylistManager::ItemsLoadedForSavePlaylist(const SongList &songs, const QString &filename, const Playlist::Path path_type) { - parser_->Save(future.result(), filename, path_type); + parser_->Save(songs, filename, path_type); } diff --git a/src/playlist/playlistmanager.h b/src/playlist/playlistmanager.h index 173b26bd7..18bced518 100644 --- a/src/playlist/playlistmanager.h +++ b/src/playlist/playlistmanager.h @@ -230,7 +230,7 @@ class PlaylistManager : public PlaylistManagerInterface { void OneOfPlaylistsChanged(); void UpdateSummaryText(); void SongsDiscovered(const SongList& songs); - void ItemsLoadedForSavePlaylist(QFuture future, const QString& filename, const Playlist::Path path_type); + void ItemsLoadedForSavePlaylist(const SongList &songs, const QString& filename, const Playlist::Path path_type); void PlaylistLoaded(); private: diff --git a/src/qobuz/qobuzrequest.cpp b/src/qobuz/qobuzrequest.cpp index 80c86e211..4ff982781 100644 --- a/src/qobuz/qobuzrequest.cpp +++ b/src/qobuz/qobuzrequest.cpp @@ -757,7 +757,7 @@ void QobuzRequest::FlushAlbumSongsRequests() { if (request.offset > 0) params << Param("offset", QString::number(request.offset)); QNetworkReply *reply = CreateRequest(QString("album/get"), params); replies_ << reply; - QObject::connect(reply, &QNetworkReply::finished, [this, reply, request] { AlbumSongsReplyReceived(reply, request.artist_id, request.album_id, request.offset, request.album_artist, request.album); }); + QObject::connect(reply, &QNetworkReply::finished, [this, reply, request]() { AlbumSongsReplyReceived(reply, request.artist_id, request.album_id, request.offset, request.album_artist, request.album); }); } diff --git a/src/smartplaylists/playlistgeneratorinserter.cpp b/src/smartplaylists/playlistgeneratorinserter.cpp index 09a6bc08f..e3397bc4c 100644 --- a/src/smartplaylists/playlistgeneratorinserter.cpp +++ b/src/smartplaylists/playlistgeneratorinserter.cpp @@ -23,8 +23,9 @@ #include #include #include +#include +#include -#include "core/closure.h" #include "core/taskmanager.h" #include "playlist/playlist.h" @@ -67,13 +68,17 @@ void PlaylistGeneratorInserter::Load(Playlist *destination, const int row, const QObject::connect(generator.get(), &PlaylistGenerator::Error, this, &PlaylistGeneratorInserter::Error); QFuture future = QtConcurrent::run(PlaylistGeneratorInserter::Generate, generator, dynamic_count); - NewClosure(future, this, SLOT(Finished(QFuture)), future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, &PlaylistGeneratorInserter::Finished); } -void PlaylistGeneratorInserter::Finished(QFuture future) { +void PlaylistGeneratorInserter::Finished() { - PlaylistItemList items = future.result(); + QFutureWatcher *watcher = static_cast*>(sender()); + PlaylistItemList items = watcher->result(); + watcher->deleteLater(); if (items.isEmpty()) { if (is_dynamic_) { diff --git a/src/smartplaylists/playlistgeneratorinserter.h b/src/smartplaylists/playlistgeneratorinserter.h index 637f33d73..317c610c9 100644 --- a/src/smartplaylists/playlistgeneratorinserter.h +++ b/src/smartplaylists/playlistgeneratorinserter.h @@ -24,7 +24,6 @@ #include "config.h" #include -#include #include #include "playlist/playlist.h" @@ -52,7 +51,7 @@ class PlaylistGeneratorInserter : public QObject { void PlayRequested(QModelIndex idx, Playlist::AutoScroll autoscroll); private slots: - void Finished(QFuture future); + void Finished(); private: TaskManager *task_manager_; diff --git a/src/smartplaylists/smartplaylistsearchpreview.cpp b/src/smartplaylists/smartplaylistsearchpreview.cpp index 35b7247e9..7886861b4 100644 --- a/src/smartplaylists/smartplaylistsearchpreview.cpp +++ b/src/smartplaylists/smartplaylistsearchpreview.cpp @@ -26,11 +26,12 @@ #include #include #include +#include +#include #include "smartplaylistsearchpreview.h" #include "ui_smartplaylistsearchpreview.h" -#include "core/closure.h" #include "playlist/playlist.h" #include "playlistquerygenerator.h" @@ -115,11 +116,17 @@ void SmartPlaylistSearchPreview::RunSearch(const SmartPlaylistSearch &search) { ui_->busy_container->show(); ui_->count_label->hide(); QFuture future = QtConcurrent::run(DoRunSearch, generator_); - NewClosure(future, this, SLOT(SearchFinished(QFuture)), future); + QFutureWatcher *watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcher::finished, this, &SmartPlaylistSearchPreview::SearchFinished); } -void SmartPlaylistSearchPreview::SearchFinished(QFuture future) { +void SmartPlaylistSearchPreview::SearchFinished() { + + QFutureWatcher *watcher = static_cast*>(sender()); + PlaylistItemList all_items = watcher->result(); + watcher->deleteLater(); last_search_ = std::dynamic_pointer_cast(generator_)->search(); generator_.reset(); @@ -132,7 +139,6 @@ void SmartPlaylistSearchPreview::SearchFinished(QFuture future return; } - PlaylistItemList all_items = future.result(); PlaylistItemList displayed_items = all_items.mid(0, PlaylistGenerator::kDefaultLimit); model_->Clear(); diff --git a/src/smartplaylists/smartplaylistsearchpreview.h b/src/smartplaylists/smartplaylistsearchpreview.h index b0dfa72ca..601fdbc45 100644 --- a/src/smartplaylists/smartplaylistsearchpreview.h +++ b/src/smartplaylists/smartplaylistsearchpreview.h @@ -24,7 +24,6 @@ #include "config.h" #include -#include #include #include "smartplaylistsearch.h" @@ -56,7 +55,7 @@ class SmartPlaylistSearchPreview : public QWidget { void RunSearch(const SmartPlaylistSearch &search); private slots: - void SearchFinished(QFuture future); + void SearchFinished(); private: Ui_SmartPlaylistSearchPreview *ui_; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 792f9eb17..d8bd2552e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -152,7 +152,6 @@ endmacro(add_test_file) add_test_file(src/utilities_test.cpp false) add_test_file(src/concurrentrun_test.cpp false) -add_test_file(src/closure_test.cpp false) add_test_file(src/mergedproxymodel_test.cpp false) add_test_file(src/sqlite_test.cpp false) add_test_file(src/tagreader_test.cpp false) diff --git a/tests/src/closure_test.cpp b/tests/src/closure_test.cpp deleted file mode 100644 index 9465dd5d7..000000000 --- a/tests/src/closure_test.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "config.h" - -#include -#include - -#include - -#include -#include -#include -#include - -#include "core/closure.h" -#include "test_utils.h" - -TEST(ClosureTest, ClosureInvokesReceiver) { - - TestQObject sender; - TestQObject receiver; - _detail::ClosureBase* closure = NewClosure(&sender, SIGNAL(Emitted()), &receiver, SLOT(Invoke())); - Q_UNUSED(closure); - EXPECT_EQ(0, receiver.invoked()); - sender.Emit(); - EXPECT_EQ(1, receiver.invoked()); - -} - -TEST(ClosureTest, ClosureDeletesSelf) { - - TestQObject sender; - TestQObject receiver; - _detail::ClosureBase* closure = NewClosure(&sender, SIGNAL(Emitted()), &receiver, SLOT(Invoke())); - _detail::ObjectHelper* helper = closure->helper(); - QSignalSpy spy(helper, SIGNAL(destroyed())); - EXPECT_EQ(0, receiver.invoked()); - sender.Emit(); - EXPECT_EQ(1, receiver.invoked()); - - EXPECT_EQ(0, spy.count()); - QEventLoop loop; - QObject::connect(helper, SIGNAL(destroyed()), &loop, SLOT(quit())); - loop.exec(); - EXPECT_EQ(1, spy.count()); - -} - -TEST(ClosureTest, ClosureDoesNotCrashWithSharedPointerSender) { - - TestQObject receiver; - TestQObject* sender; - std::unique_ptr spy; - QPointer<_detail::ObjectHelper> closure; - { - QSharedPointer sender_shared(new TestQObject); - sender = sender_shared.data(); - closure = QPointer<_detail::ObjectHelper>(NewClosure(sender_shared, SIGNAL(Emitted()), &receiver, SLOT(Invoke()))->helper()); - spy.reset(new QSignalSpy(sender, SIGNAL(destroyed()))); - } - ASSERT_EQ(0, receiver.invoked()); - sender->Emit(); - ASSERT_EQ(1, receiver.invoked()); - - ASSERT_EQ(0, spy->count()); - QEventLoop loop; - QObject::connect(sender, SIGNAL(destroyed()), &loop, SLOT(quit())); - loop.exec(); - ASSERT_EQ(1, spy->count()); - EXPECT_TRUE(closure.isNull()); - -} - -namespace { - -void Foo(bool* called, int question, int* answer) { - - *called = true; - *answer = question; - -} - -} // namespace - -TEST(ClosureTest, ClosureWorksWithFunctionPointers) { - - TestQObject sender; - bool called = false; - int question = 42; - int answer = 0; - NewClosure(&sender, SIGNAL(Emitted()), &Foo, &called, question, &answer); - EXPECT_FALSE(called); - sender.Emit(); - EXPECT_TRUE(called); - EXPECT_EQ(question, answer); - -} - -TEST(ClosureTest, ClosureWorksWithStandardFunctions) { - - TestQObject sender; - bool called = false; - int question = 42; - int answer = 0; - std::function callback(&Foo); - NewClosure(&sender, SIGNAL(Emitted()), callback, &called, question, &answer); - EXPECT_FALSE(called); - sender.Emit(); - EXPECT_TRUE(called); - EXPECT_EQ(question, answer); - -} - -namespace { - -class Bar { - public: - explicit Bar(int a) : foo_(a) {} - bool Foo(int* answer) { - *answer = foo_; - return true; - } - - private: - int foo_; -}; - -} - -TEST(ClosureTest, ClosureWorksWithMemberFunctionPointers) { - - TestQObject sender; - Bar receiver(42); - int q = 1; - NewClosure(&sender, SIGNAL(Emitted()), &receiver, &Bar::Foo, &q); - EXPECT_EQ(1, q); - sender.Emit(); - EXPECT_EQ(42, q); - -} - -TEST(ClosureTest, ClosureCallsLambda) { - - TestQObject sender; - bool called = false; - NewClosure(&sender, SIGNAL(Emitted()), [&called] () { called = true; }); - EXPECT_FALSE(called); - sender.Emit(); - EXPECT_TRUE(called); - -}