diff --git a/loader/include/Geode/utils/Task.hpp b/loader/include/Geode/utils/Task.hpp index c5a7bc7b..bc06297e 100644 --- a/loader/include/Geode/utils/Task.hpp +++ b/loader/include/Geode/utils/Task.hpp @@ -561,7 +561,7 @@ namespace geode { * @param resultMapper Function that converts the finished values of * the mapped Task to a desired type. Note that the function is only * given a pointer to the finish value, as `T` is not guaranteed to be - * copiable - the mapper may NOT move out of the value! + * copyable - the mapper may NOT move out of the value! * @param progressMapper Function that converts the progress values of * the mapped Task to a desired type * @param onCancelled Function that is called if the mapped Task is @@ -634,7 +634,7 @@ namespace geode { * @param resultMapper Function that converts the finished values of * the mapped Task to a desired type. Note that the function is only * given a pointer to the finish value, as `T` is not guaranteed to be - * copiable - the mapper may NOT move out of the value! + * copyable - the mapper may NOT move out of the value! * @param progressMapper Function that converts the progress values of * the mapped Task to a desired type * @param name The name of the Task; used for debugging. The name of @@ -653,7 +653,7 @@ namespace geode { * @param resultMapper Function that converts the finished values of * the mapped Task to a desired type. Note that the function is only * given a pointer to the finish value, as `T` is not guaranteed to be - * copiable - the mapper may NOT move out of the value! + * copyable - the mapper may NOT move out of the value! * @param name The name of the Task; used for debugging. The name of * the mapped task is appended to the end */ template <class ResultMapper> @@ -662,6 +662,84 @@ namespace geode { return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name); } + /** + * Creates an implicit event listener for this Task that will call the + * provided functions when the Task finishes, progresses, or is cancelled. + * The listener will automatically be destroyed after the Task has finished. + * @param onResult Function to call when the Task finishes. The function + * is given a pointer to the finished value, `T*`. + * @param onProgress Function to call when the Task progresses. The function + * is given a pointer to the progress value, `P*`. + * @param onCancelled Function to call when the Task is cancelled + * + * @warning This method should only be used in a global context. If you rely + * on some node still existing when the task completes, use an event listener instead. + */ + template <class OnResult, class OnProgress, class OnCancelled> + void then(OnResult&& onResult, OnProgress&& onProgress, OnCancelled&& onCancelled) const { + // use a raw pointer to avoid cyclic references, + // we destroy it manually later on + auto* listener = new EventListener<Task>(*this); + listener->bind([ + onResult = std::move(onResult), + onProgress = std::move(onProgress), + onCancelled = std::move(onCancelled), + listener + ](Event* event) mutable { + bool finished = false; + if (auto v = event->getValue()) { + finished = true; + onResult(v); + } + else if (auto p = event->getProgress()) { + onProgress(p); + } + else if (event->isCancelled()) { + finished = true; + onCancelled(); + } + if (finished) { + // delay destroying the listener for a frame + // to prevent any potential use-after-free + queueInMainThread([listener] { + delete listener; + }); + } + }); + } + + /** + * Creates an implicit event listener for this Task that will call the + * provided functions when the Task finishes or progresses. + * The listener will automatically be destroyed after the Task has finished. + * @param onResult Function to call when the Task finishes. The function + * is given a pointer to the finished value, `T*`. + * @param onProgress Function to call when the Task progresses. The function + * is given a pointer to the progress value, `P*`. + * + * @warning This method should only be used in a global context. If you rely + * on some node still existing when the task completes, use an event listener instead. + */ + template <class OnResult, class OnProgress> + void then(OnResult&& onResult, OnProgress&& onProgress) const { + this->then(std::move(onResult), std::move(onProgress), [] {}); + } + + /** + * Creates an implicit event listener for this Task that will call the + * provided functions when the Task finishes. + * The listener will automatically be destroyed after the Task has finished. + * @param onResult Function to call when the Task finishes. The function + * is given a pointer to the finished value, `T*`. + * + * @warning This method should only be used in a global context. If you rely + * on some node still existing when the task completes, use an event listener instead. + */ + template <class OnResult, class OnProgress> + void then(OnResult&& onResult) const { + this->then(std::move(onResult), [](auto const&) {}, [] {}); + } + ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) { if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) { fn(e);