mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-27 13:53:01 +00:00
295 lines
9.3 KiB
C++
295 lines
9.3 KiB
C++
// AUTOGENERATED COPYRIGHT HEADER START
|
|
// Copyright (C) 2020-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
// Copyright (C) 2021 tytan652 <tytan652@tytanium.xyz>
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
|
|
|
#include "ui.hpp"
|
|
#include "strings.hpp"
|
|
#include "ui-common.hpp"
|
|
#include "configuration.hpp"
|
|
#include "obs/obs-tools.hpp"
|
|
#include "plugin.hpp"
|
|
#include "ui/ui-obs-browser-widget.hpp"
|
|
|
|
#include "warning-disable.hpp"
|
|
#include <string_view>
|
|
#include "warning-enable.hpp"
|
|
|
|
// Translation Keys
|
|
constexpr std::string_view _i18n_prefix = "StreamFX::";
|
|
constexpr std::string_view _i18n_menu = "UI.Menu";
|
|
constexpr std::string_view _i18n_menu_support = "UI.Menu.Support";
|
|
constexpr std::string_view _i18n_menu_wiki = "UI.Menu.Wiki";
|
|
constexpr std::string_view _i18n_menu_website = "UI.Menu.Website";
|
|
constexpr std::string_view _i18n_menu_discord = "UI.Menu.Discord";
|
|
constexpr std::string_view _i18n_menu_youtube = "UI.Menu.YouTube";
|
|
constexpr std::string_view _i18n_menu_twitter = "UI.Menu.Twitter";
|
|
constexpr std::string_view _i18n_menu_github = "UI.Menu.Github";
|
|
constexpr std::string_view _i18n_menu_about = "UI.Menu.About";
|
|
|
|
// Configuration
|
|
constexpr std::string_view _cfg_have_shown_about = "UI.HaveShownAboutStreamFX";
|
|
|
|
// URLs
|
|
constexpr std::string_view _url_support = "https://s.xaymar.com/streamfx-dc-support";
|
|
constexpr std::string_view _url_wiki = "https://github.com/Xaymar/obs-StreamFX/wiki";
|
|
constexpr std::string_view _url_website = "https://streamfx.xaymar.com";
|
|
constexpr std::string_view _url_discord = "https://s.xaymar.com/streamfx-dc";
|
|
constexpr std::string_view _url_twitter = "https://s.xaymar.com/streamfx-tw";
|
|
constexpr std::string_view _url_youtube = "https://s.xaymar.com/streamfx-yt";
|
|
|
|
static std::shared_ptr<streamfx::ui::obs_browser_cef> _obs_browser_cef;
|
|
|
|
inline void qt_init_resource()
|
|
{
|
|
Q_INIT_RESOURCE(streamfx);
|
|
}
|
|
|
|
inline void qt_cleanup_resource()
|
|
{
|
|
Q_CLEANUP_RESOURCE(streamfx);
|
|
}
|
|
|
|
bool streamfx::ui::handler::have_shown_about_streamfx(bool shown)
|
|
{
|
|
auto config = streamfx::configuration::instance();
|
|
auto data = config->get();
|
|
if (shown) {
|
|
obs_data_set_bool(data.get(), _cfg_have_shown_about.data(), true);
|
|
config->save();
|
|
}
|
|
if (config->is_different_version()) {
|
|
return false;
|
|
} else {
|
|
return obs_data_get_bool(data.get(), _cfg_have_shown_about.data());
|
|
}
|
|
}
|
|
|
|
streamfx::ui::handler::handler()
|
|
: QObject(), _menu_action(), _menu(),
|
|
|
|
_action_support(), _action_wiki(), _action_website(), _action_discord(), _action_twitter(), _action_youtube(),
|
|
|
|
_about_action(), _about_dialog(),
|
|
|
|
_translator(), _updater()
|
|
{
|
|
obs_frontend_add_event_callback(frontend_event_handler, this);
|
|
}
|
|
|
|
streamfx::ui::handler::~handler()
|
|
{
|
|
obs_frontend_remove_event_callback(frontend_event_handler, this);
|
|
}
|
|
|
|
void streamfx::ui::handler::frontend_event_handler(obs_frontend_event event, void* private_data)
|
|
{
|
|
streamfx::ui::handler* ptr = reinterpret_cast<streamfx::ui::handler*>(private_data);
|
|
switch (event) {
|
|
case OBS_FRONTEND_EVENT_FINISHED_LOADING:
|
|
ptr->on_obs_loaded();
|
|
break;
|
|
case OBS_FRONTEND_EVENT_EXIT:
|
|
ptr->on_obs_exit();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void streamfx::ui::handler::on_obs_loaded()
|
|
{
|
|
// Initialize the required Qt resources.
|
|
qt_init_resource();
|
|
|
|
// Add our own translation helper to the Qt Application.
|
|
_translator = new streamfx::ui::translator(this);
|
|
QCoreApplication::installTranslator(_translator);
|
|
|
|
// Pre-load CEF if available.
|
|
if (streamfx::ui::obs_browser_widget::is_available()) {
|
|
try {
|
|
_obs_browser_cef = streamfx::ui::obs_browser_cef::instance();
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
// Create the 'About StreamFX' dialog.
|
|
_about_dialog = new streamfx::ui::about();
|
|
|
|
{ // Create and build the StreamFX menu
|
|
_menu = new QMenu(reinterpret_cast<QWidget*>(obs_frontend_get_main_window()));
|
|
|
|
// Wiki
|
|
// Help & Support
|
|
// ---
|
|
// Website
|
|
// Discord
|
|
// Twitter
|
|
// YouTube
|
|
// <--->
|
|
// <Updater>
|
|
// ---
|
|
// About StreamFX
|
|
|
|
{
|
|
// Wiki
|
|
_action_wiki = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_wiki.data())));
|
|
_action_wiki->setMenuRole(QAction::NoRole);
|
|
connect(_action_wiki, &QAction::triggered, this, &streamfx::ui::handler::on_action_wiki);
|
|
|
|
// Help & Support
|
|
_action_support = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_support.data())));
|
|
_action_support->setMenuRole(QAction::NoRole);
|
|
connect(_action_support, &QAction::triggered, this, &streamfx::ui::handler::on_action_support);
|
|
}
|
|
|
|
_menu->addSeparator();
|
|
{
|
|
_action_website = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_website.data())));
|
|
_action_website->setMenuRole(QAction::NoRole);
|
|
connect(_action_website, &QAction::triggered, this, &streamfx::ui::handler::on_action_website);
|
|
|
|
_action_discord = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_discord.data())));
|
|
_action_discord->setMenuRole(QAction::NoRole);
|
|
connect(_action_discord, &QAction::triggered, this, &streamfx::ui::handler::on_action_discord);
|
|
|
|
_action_twitter = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_twitter.data())));
|
|
_action_twitter->setMenuRole(QAction::NoRole);
|
|
connect(_action_twitter, &QAction::triggered, this, &streamfx::ui::handler::on_action_twitter);
|
|
|
|
_action_youtube = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_youtube.data())));
|
|
_action_youtube->setMenuRole(QAction::NoRole);
|
|
connect(_action_youtube, &QAction::triggered, this, &streamfx::ui::handler::on_action_youtube);
|
|
}
|
|
|
|
// Create the updater.
|
|
_updater = streamfx::ui::updater::instance(_menu);
|
|
|
|
_menu->addSeparator();
|
|
|
|
// About
|
|
_about_action = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_about.data())));
|
|
_about_action->setMenuRole(QAction::NoRole);
|
|
connect(_about_action, &QAction::triggered, this, &streamfx::ui::handler::on_action_about);
|
|
}
|
|
|
|
{ // Add an actual Menu entry.
|
|
QMainWindow* main_widget = reinterpret_cast<QMainWindow*>(obs_frontend_get_main_window());
|
|
_menu_action = new QAction(main_widget);
|
|
_menu_action->setMenuRole(QAction::NoRole);
|
|
_menu_action->setMenu(_menu);
|
|
_menu_action->setText(QString::fromUtf8(D_TRANSLATE(_i18n_menu.data())));
|
|
|
|
// For unknown reasons, the "About StreamFX" menu replaces the OBS about menu.
|
|
QList<QMenu*> obs_menus = main_widget->menuBar()->findChildren<QMenu*>(QString(), Qt::FindDirectChildrenOnly);
|
|
if (QMenu* help_menu = obs_menus.at(1); help_menu) {
|
|
main_widget->menuBar()->insertAction(help_menu->menuAction(), _menu_action);
|
|
} else {
|
|
main_widget->menuBar()->addAction(_menu_action);
|
|
}
|
|
}
|
|
|
|
// Show the 'About StreamFX' dialog if that has not happened yet.
|
|
if (!have_shown_about_streamfx()) {
|
|
// Automatically show it if it has not yet been shown.
|
|
_about_dialog->show();
|
|
have_shown_about_streamfx(true);
|
|
}
|
|
|
|
// Let the Updater start its work.
|
|
this->_updater->obs_ready();
|
|
}
|
|
|
|
void streamfx::ui::handler::on_obs_exit()
|
|
{
|
|
// Release CEF
|
|
_obs_browser_cef.reset();
|
|
|
|
// Remove translator.
|
|
QCoreApplication::removeTranslator(_translator);
|
|
|
|
// Clean up any Qt resources we added.
|
|
qt_cleanup_resource();
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_support(bool)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_support.data())));
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_wiki(bool)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_wiki.data())));
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_website(bool)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_website.data())));
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_discord(bool)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_discord.data())));
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_twitter(bool)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_twitter.data())));
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_youtube(bool)
|
|
{
|
|
QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_youtube.data())));
|
|
}
|
|
|
|
void streamfx::ui::handler::on_action_about(bool checked)
|
|
{
|
|
_about_dialog->show();
|
|
}
|
|
|
|
std::shared_ptr<streamfx::ui::handler> streamfx::ui::handler::instance()
|
|
{
|
|
static std::weak_ptr<streamfx::ui::handler> winst;
|
|
static std::mutex mtx;
|
|
|
|
std::unique_lock<decltype(mtx)> lock(mtx);
|
|
auto instance = winst.lock();
|
|
if (!instance) {
|
|
instance = std::shared_ptr<streamfx::ui::handler>(new streamfx::ui::handler());
|
|
winst = instance;
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
streamfx::ui::translator::translator(QObject* parent) {}
|
|
|
|
streamfx::ui::translator::~translator() {}
|
|
|
|
QString streamfx::ui::translator::translate(const char* context, const char* sourceText, const char* disambiguation, int n) const
|
|
{
|
|
if (sourceText) {
|
|
std::string_view sourceView{sourceText};
|
|
if (sourceView.substr(0, _i18n_prefix.length()) == _i18n_prefix) {
|
|
return QString::fromUtf8(D_TRANSLATE(sourceView.substr(_i18n_prefix.length()).data()));
|
|
}
|
|
}
|
|
if (disambiguation) {
|
|
std::string_view disambiguationView{disambiguation};
|
|
if (disambiguationView.substr(0, _i18n_prefix.length()) == _i18n_prefix) {
|
|
return QString::fromUtf8(D_TRANSLATE(disambiguationView.substr(_i18n_prefix.length()).data()));
|
|
}
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
static std::shared_ptr<streamfx::ui::handler> loader_instance;
|
|
|
|
static auto loader = streamfx::loader(
|
|
[]() { // Initalizer
|
|
loader_instance = streamfx::ui::handler::instance();
|
|
},
|
|
[]() { // Finalizer
|
|
loader_instance.reset();
|
|
},
|
|
streamfx::loader_priority::LOWEST); // Must be loaded after all other functionality.
|