Files
ts-qt/modern-qt/utility/painter-resource.hh
2025-10-20 00:32:01 +08:00

123 lines
4.3 KiB
C++

#pragma once
#include <qnetworkaccessmanager.h>
#include <qnetworkreply.h>
#include <qpixmap.h>
namespace creeper {
namespace painter_resource {
template <typename T>
concept finished_callback_c = std::invocable<T> || std::invocable<T, QPixmap&>;
}
struct PainterResource : public QPixmap {
constexpr explicit PainterResource(std::string_view url) noexcept
: QPixmap {} {
const auto qurl = QUrl(QString::fromUtf8(url.data(), static_cast<int>(url.size())));
if (is_filesystem_url(url) || is_qt_resource_url(url)) {
qDebug() << "[PainterResource] is_filesystem_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<decltype(f)>
{
const auto qurl = QUrl(QString::fromUtf8(url.data(), static_cast<int>(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 <typename T>
explicit PainterResource(T&& other) noexcept
requires std::convertible_to<T, QPixmap>
: QPixmap(std::forward<T>(other)) { }
template <typename T>
auto operator=(T&& other) noexcept -> PainterResource&
requires std::convertible_to<T, QPixmap>
{
QPixmap::operator=(std::forward<T>(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<PainterResource&> auto&& f) {
finished_callback_ = std::forward<decltype(f)>(f);
}
private:
std::optional<std::function<void(PainterResource&)>> finished_callback_;
bool is_loading_ = false;
bool is_error_ = false;
std::shared_ptr<bool> resource_exiting = std::make_shared<bool>(true);
auto download_resource_from_network(const QUrl& url, auto&& f) noexcept -> void
requires painter_resource::finished_callback_c<decltype(f)>
{
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<F, PainterResource&>) std::invoke(f, *this);
if constexpr (std::invocable<F>) 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://");
}
};
}