#pragma once #include #include #include namespace creeper { namespace painter_resource { template concept finished_callback_c = std::invocable || std::invocable; } struct PainterResource : public QPixmap { constexpr explicit PainterResource(std::string_view url) noexcept : QPixmap {} { const auto qurl = QUrl(QString::fromUtf8(url.data(), static_cast(url.size()))); if (is_filesystem_url(url) || is_qt_resource_url(url)) { QPixmap::load(qurl.path()); } else if (is_network_url(url)) { download_resource_from_network(qurl, [](auto&) { }); } else { qWarning() << "[PainterResource] Failed to recognize the type of url"; } } constexpr explicit PainterResource(std::string_view url, auto&& f) noexcept requires painter_resource::finished_callback_c { const auto qurl = QUrl(QString::fromUtf8(url.data(), static_cast(url.size()))); if (is_network_url(url)) { download_resource_from_network(qurl, f); } else { qWarning() << "[PainterResource] Only network url can be used with callback"; } } ~PainterResource() noexcept { *resource_exiting = false; } template explicit PainterResource(T&& other) noexcept requires std::convertible_to : QPixmap(std::forward(other)) { } template auto operator=(T&& other) noexcept -> PainterResource& requires std::convertible_to { QPixmap::operator=(std::forward(other)); return *this; } auto is_loading() const noexcept -> bool { return is_loading_; } auto is_error() const noexcept -> bool { return is_error_; } auto add_finished_callback(std::invocable auto&& f) { finished_callback_ = std::forward(f); } private: std::optional> finished_callback_; bool is_loading_ = false; bool is_error_ = false; std::shared_ptr resource_exiting = std::make_shared(true); auto download_resource_from_network(const QUrl& url, auto&& f) noexcept -> void requires painter_resource::finished_callback_c { is_loading_ = true; auto manager = new QNetworkAccessManager; auto replay = manager->get(QNetworkRequest { url }); auto resource_exiting = this->resource_exiting; QObject::connect(replay, &QNetworkReply::finished, [=, this] { if (!*resource_exiting) { qWarning() << "[PainterResource] Async task aborted: " "Resource instance has been destroyed."; return; } const auto error = replay->error(); const auto data = replay->readAll(); if (error != QNetworkReply::NoError) { is_error_ = true; qWarning() << "[PainterResource] Network error:" << replay->errorString(); } else if (data.isNull()) { is_error_ = true; } else { is_error_ = false; loadFromData(data); } is_loading_ = false; manager->deleteLater(); using F = decltype(f); if constexpr (std::invocable) std::invoke(f, *this); if constexpr (std::invocable) std::invoke(f); if (finished_callback_) std::invoke(*finished_callback_, *this); }); } static constexpr auto starts_with(std::string_view s, std::string_view prefix) -> bool { return s.substr(0, prefix.size()) == prefix; } static constexpr auto is_filesystem_url(std::string_view url) -> bool { return !starts_with(url, "http://") && !starts_with(url, "https://") && !starts_with(url, "qrc:/") && !starts_with(url, ":/"); } static constexpr auto is_qt_resource_url(std::string_view url) -> bool { return starts_with(url, "qrc:/") || starts_with(url, ":/"); } static constexpr auto is_network_url(std::string_view url) -> bool { return starts_with(url, "http://") || starts_with(url, "https://"); } }; }