// AUTOGENERATED COPYRIGHT HEADER START // Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks // Copyright (C) 2022 lainon // AUTOGENERATED COPYRIGHT HEADER END #include "ui-about.hpp" #include "ui-common.hpp" #include "plugin.hpp" #include "ui-about-entry.hpp" #include "util/util-logging.hpp" #include "warning-disable.hpp" #include #include #include #include #include #include #include "warning-enable.hpp" #ifdef _DEBUG #define ST_PREFIX "<%s> " #define D_LOG_ERROR(x, ...) P_LOG_ERROR(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__) #define D_LOG_WARNING(x, ...) P_LOG_WARN(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__) #define D_LOG_INFO(x, ...) P_LOG_INFO(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__) #define D_LOG_DEBUG(x, ...) P_LOG_DEBUG(ST_PREFIX##x, __FUNCTION_SIG__, __VA_ARGS__) #else #define ST_PREFIX " " #define D_LOG_ERROR(...) P_LOG_ERROR(ST_PREFIX __VA_ARGS__) #define D_LOG_WARNING(...) P_LOG_WARN(ST_PREFIX __VA_ARGS__) #define D_LOG_INFO(...) P_LOG_INFO(ST_PREFIX __VA_ARGS__) #define D_LOG_DEBUG(...) P_LOG_DEBUG(ST_PREFIX __VA_ARGS__) #endif static const std::vector _thankyous = { ":/thankyou/thankyou_cat", ":/thankyou/thankyou_otter", ":/thankyou/thankyou_fox", }; streamfx::ui::about::about() : QDialog(reinterpret_cast(obs_frontend_get_main_window())) { std::deque entries; // Set-up UI. setupUi(this); setWindowFlag(Qt::WindowContextHelpButtonHint, false); // Remove Help button. content->setSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Maximum); // Create a uniform std::random_device rd; std::mt19937_64 generator(rd()); std::uniform_int_distribution rnd; // Load entries from 'thanks.json' try { auto file = streamfx::data_file_path("thanks.json"); D_LOG_DEBUG("Attempting to load '%s'...", file.generic_string().c_str()); if (!std::filesystem::exists(file)) { // Crash if this file is missing. throw std::runtime_error("File 'thanks.json' is missing."); } std::ifstream fils{file}; if (fils.bad() || fils.eof()) { // Crash if this file is corrupt. throw std::runtime_error("File 'thanks.json' is corrupted."); } auto data = nlohmann::json::parse(fils); if (auto iter = data.find("contributor"); iter != data.end()) { D_LOG_DEBUG(" Found %" PRIu64 " contributor entries.", iter->size()); auto kvs = iter->items(); for (auto kv : kvs) { D_LOG_DEBUG(" '%s' => '%s'", kv.key().c_str(), kv.value().get().c_str()); entries.push_back( ui::about::entry{kv.key(), role_type::CONTRIBUTOR, "", kv.value().get()}); } } if (auto iter = data.find("translator"); iter != data.end()) { D_LOG_DEBUG(" Found %" PRIu64 " translator entries.", iter->size()); auto kvs = iter->items(); for (auto kv : kvs) { D_LOG_DEBUG(" '%s' => '%s'", kv.key().c_str(), kv.value().get().c_str()); entries.push_back(ui::about::entry{kv.key(), role_type::TRANSLATOR, "", kv.value().get()}); } } if (auto iter = data.find("supporter"); iter != data.end()) { auto data2 = *iter; if (auto iter2 = data2.find("github"); iter2 != data2.end()) { D_LOG_DEBUG(" Found %" PRIu64 " GitHub supporter entries.", iter2->size()); auto kvs = iter2->items(); for (auto kv : kvs) { D_LOG_DEBUG(" '%s' => '%s'", kv.key().c_str(), kv.value().get().c_str()); entries.push_back( ui::about::entry{kv.key(), role_type::SUPPORTER, "GitHub", kv.value().get()}); } } if (auto iter2 = data2.find("patreon"); iter2 != data2.end()) { D_LOG_DEBUG(" Found %" PRIu64 " Patreon supporter entries.", iter2->size()); auto kvs = iter2->items(); for (auto kv : kvs) { D_LOG_DEBUG(" '%s' => '%s'", kv.key().c_str(), kv.value().get().c_str()); entries.push_back( ui::about::entry{kv.key(), role_type::SUPPORTER, "Patreon", kv.value().get()}); } } } } catch (const std::exception& ex) { D_LOG_ERROR("Loading '%s' failed with error: %s", "thanks.json", ex.what()); throw std::runtime_error("File 'thanks.json' is invalid."); } // Build a grid of random entries. { QGridLayout* layout = dynamic_cast(content->layout()); int row = 0; int column = 0; int thanks = 0; // Fix columns being stretched for no reason. layout->setColumnStretch(0, 1); layout->setColumnStretch(1, 1); D_LOG_DEBUG("Building grid of Thank You entries...", ""); // Randomize the list. std::shuffle(entries.begin(), entries.end(), generator); for (auto entry : entries) { // Create a new entry. streamfx::ui::about_entry* v = new streamfx::ui::about_entry(content, entry); layout->addWidget(v, row, column); layout->setRowStretch(row, 0); D_LOG_DEBUG(" Added '%s' => '%s'.", entry.name.c_str(), entry.link.c_str()); // Proceed down the grid. column += 1; if (column >= 2) { column = 0; row += 1; thanks += 1; if (thanks % 9 == 8) { // "Thank you" every 8 rows. auto image = new QLabel(content); auto idx = rnd(generator) % _thankyous.size(); image->setPixmap(QPixmap(_thankyous.at(idx).data())); image->setScaledContents(true); image->setFixedSize(384, 384); layout->addWidget(image, row, 0, 1, 2, Qt::AlignTop | Qt::AlignHCenter); layout->setRowStretch(row, 0); thanks = 0; row += 1; } } } { // Fix weird automatic scaling done by Qt at the end of the list. if (column != 0) { row += 1; column = 0; } auto padder = new QFrame(content); auto sp = padder->sizePolicy(); sp.setVerticalPolicy(QSizePolicy::Minimum); sp.setVerticalStretch(1); padder->setSizePolicy(sp); padder->setObjectName("PaddleMeDaddy"); padder->setMaximumHeight(QWIDGETSIZE_MAX); padder->setMinimumHeight(1); padder->setFrameShape(QFrame::NoFrame); layout->addWidget(padder, row, 0, 1, 2); layout->setRowStretch(row, std::numeric_limits::max()); } } // Update the Version information. version->setText(STREAMFX_VERSION_STRING); // Make the OK button do things. connect(buttonBox, &QDialogButtonBox::accepted, this, &streamfx::ui::about::on_ok); } streamfx::ui::about::~about() {} void streamfx::ui::about::on_ok() { hide(); }