project: Implement a brand new UI with fancy windows
Implements support for various new UI features that weren't possible up until now, such as an 'About StreamFX' window with a thank you to everyone that supported the project up until now.
|  | @ -501,8 +501,21 @@ endif() | ||||||
| ## OBS Studio - Frontend/Qt | ## OBS Studio - Frontend/Qt | ||||||
| if(HAVE_OBS_FRONTEND)	 | if(HAVE_OBS_FRONTEND)	 | ||||||
| 	list(APPEND PROJECT_UI | 	list(APPEND PROJECT_UI | ||||||
|  | 		"ui/streamfx.qrc" | ||||||
|  | 		"ui/about.ui" | ||||||
|  | 		"ui/about-entry.ui" | ||||||
| 	) | 	) | ||||||
| 	list(APPEND PROJECT_PRIVATE_SOURCE | 	list(APPEND PROJECT_PRIVATE_SOURCE | ||||||
|  | 		"source/ui/ui-common.hpp" | ||||||
|  | 		"source/ui/ui.hpp" | ||||||
|  | 		"source/ui/ui.cpp" | ||||||
|  | 		"source/ui/ui-about.hpp" | ||||||
|  | 		"source/ui/ui-about.cpp" | ||||||
|  | 		"source/ui/ui-about-entry.hpp" | ||||||
|  | 		"source/ui/ui-about-entry.cpp" | ||||||
|  | 	) | ||||||
|  | 	list(APPEND PROJECT_DATA | ||||||
|  | 		 | ||||||
| 	) | 	) | ||||||
| 	list(APPEND PROJECT_INCLUDE_DIRS | 	list(APPEND PROJECT_INCLUDE_DIRS | ||||||
| 		"source/ui" | 		"source/ui" | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
| #include "plugin.hpp" | #include "plugin.hpp" | ||||||
|  | #include <fstream> | ||||||
| #include <stdexcept> | #include <stdexcept> | ||||||
| #include "configuration.hpp" | #include "configuration.hpp" | ||||||
| #include "obs/obs-source-tracker.hpp" | #include "obs/obs-source-tracker.hpp" | ||||||
|  | @ -62,6 +63,10 @@ | ||||||
| #include "transitions/transition-shader.hpp" | #include "transitions/transition-shader.hpp" | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #ifdef ENABLE_FRONTEND | ||||||
|  | #include "ui/ui.hpp" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| static std::shared_ptr<util::threadpool> _threadpool; | static std::shared_ptr<util::threadpool> _threadpool; | ||||||
| 
 | 
 | ||||||
| MODULE_EXPORT bool obs_module_load(void) | MODULE_EXPORT bool obs_module_load(void) | ||||||
|  | @ -133,6 +138,11 @@ try { | ||||||
| #endif | #endif | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Frontend
 | ||||||
|  | #ifdef ENABLE_FRONTEND | ||||||
|  | 	streamfx::ui::handler::initialize(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 	LOG_INFO("Loaded Version %s", STREAMFX_VERSION_STRING); | 	LOG_INFO("Loaded Version %s", STREAMFX_VERSION_STRING); | ||||||
| 	return true; | 	return true; | ||||||
| } catch (...) { | } catch (...) { | ||||||
|  | @ -144,6 +154,11 @@ MODULE_EXPORT void obs_module_unload(void) | ||||||
| try { | try { | ||||||
| 	LOG_INFO("Unloading Version %s", STREAMFX_VERSION_STRING); | 	LOG_INFO("Unloading Version %s", STREAMFX_VERSION_STRING); | ||||||
| 
 | 
 | ||||||
|  | 	// Frontend
 | ||||||
|  | #ifdef ENABLE_FRONTEND | ||||||
|  | 	streamfx::ui::handler::finalize(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| 	// Transitions
 | 	// Transitions
 | ||||||
| 	{ | 	{ | ||||||
| 		using namespace streamfx::transition; | 		using namespace streamfx::transition; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,130 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2020 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "ui-about-entry.hpp" | ||||||
|  | 
 | ||||||
|  | constexpr std::string_view i18n_role_contributor       = "UI.About.Role.Contributor"; | ||||||
|  | constexpr std::string_view i18n_role_translator        = "UI.About.Role.Translator"; | ||||||
|  | constexpr std::string_view i18n_role_family            = "UI.About.Role.Family"; | ||||||
|  | constexpr std::string_view i18n_role_friend            = "UI.About.Role.Friend"; | ||||||
|  | constexpr std::string_view i18n_role_supporter_patreon = "UI.About.Role.Supporter.Patreon"; | ||||||
|  | constexpr std::string_view i18n_role_supporter_github  = "UI.About.Role.Supporter.Github"; | ||||||
|  | constexpr std::string_view i18n_role_supporter_twitch  = "UI.About.Role.Supporter.Twitch"; | ||||||
|  | constexpr std::string_view i18n_role_creator           = "UI.About.Role.Creator"; | ||||||
|  | 
 | ||||||
|  | streamfx::ui::about_entry::about_entry(QWidget* parent, streamfx::ui::about::entry& entry) | ||||||
|  | 	: QWidget(parent), _last_click(std::chrono::high_resolution_clock::now()), _link1_url(), _link2_url() | ||||||
|  | { | ||||||
|  | 	setupUi(this); | ||||||
|  | 
 | ||||||
|  | 	name->setText(QString::fromStdString(entry.name)); | ||||||
|  | 	switch (entry.role) { | ||||||
|  | 	case streamfx::ui::about::role_type::NONE: | ||||||
|  | 		title->setText(QString::fromStdString(entry.role_custom)); | ||||||
|  | 		break; | ||||||
|  | 	case streamfx::ui::about::role_type::CONTRIBUTOR: | ||||||
|  | 		title->setText(D_TRANSLATE(i18n_role_contributor.data())); | ||||||
|  | 		break; | ||||||
|  | 	case streamfx::ui::about::role_type::TRANSLATOR: | ||||||
|  | 		title->setText(D_TRANSLATE(i18n_role_translator.data())); | ||||||
|  | 		break; | ||||||
|  | 	case streamfx::ui::about::role_type::FAMILY: { | ||||||
|  | 		const char*       txt = D_TRANSLATE(i18n_role_family.data()); | ||||||
|  | 		std::vector<char> buf(2048); | ||||||
|  | 		snprintf(buf.data(), buf.size(), txt, entry.role_custom.c_str()); | ||||||
|  | 		title->setText(QString::fromUtf8(buf.data())); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	case streamfx::ui::about::role_type::FRIEND: { | ||||||
|  | 		const char*       txt = D_TRANSLATE(i18n_role_friend.data()); | ||||||
|  | 		std::vector<char> buf(2048); | ||||||
|  | 		snprintf(buf.data(), buf.size(), txt, entry.role_custom.c_str()); | ||||||
|  | 		title->setText(QString::fromUtf8(buf.data())); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	case streamfx::ui::about::role_type::PATREON_SUPPORTER: | ||||||
|  | 		title->setText(D_TRANSLATE(i18n_role_supporter_patreon.data())); | ||||||
|  | 		break; | ||||||
|  | 	case streamfx::ui::about::role_type::GITHUB_SUPPORTER: | ||||||
|  | 		title->setText(D_TRANSLATE(i18n_role_supporter_github.data())); | ||||||
|  | 		break; | ||||||
|  | 	case streamfx::ui::about::role_type::TWITCH_SUPPORTER: | ||||||
|  | 		title->setText(D_TRANSLATE(i18n_role_supporter_twitch.data())); | ||||||
|  | 		break; | ||||||
|  | 	case streamfx::ui::about::role_type::CREATOR: | ||||||
|  | 		title->setText(D_TRANSLATE(i18n_role_creator.data())); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	std::tuple<streamfx::ui::about::link_type, std::string, std::string, QPushButton*, QUrl&> els[]{ | ||||||
|  | 		{entry.link1_type, entry.link1_address, entry.link1_text, link1, _link1_url}, | ||||||
|  | 		{entry.link2_type, entry.link2_address, entry.link2_text, link2, _link2_url}, | ||||||
|  | 	}; | ||||||
|  | 	for (auto el : els) { | ||||||
|  | 		switch (std::get<0>(el)) { | ||||||
|  | 		case streamfx::ui::about::link_type::NONE: | ||||||
|  | 			std::get<3>(el)->setHidden(true); | ||||||
|  | 			break; | ||||||
|  | 		case streamfx::ui::about::link_type::GENERIC: | ||||||
|  | 			std::get<3>(el)->setIcon(QIcon(":/linktype/generic")); | ||||||
|  | 			break; | ||||||
|  | 		case streamfx::ui::about::link_type::SOCIAL_TWITCH: | ||||||
|  | 			std::get<3>(el)->setIcon(QIcon(":/linktype/twitch")); | ||||||
|  | 			break; | ||||||
|  | 		case streamfx::ui::about::link_type::SOCIAL_YOUTUBE: | ||||||
|  | 			std::get<3>(el)->setIcon(QIcon(":/linktype/youtube")); | ||||||
|  | 			break; | ||||||
|  | 		case streamfx::ui::about::link_type::SOCIAL_DISCORD: | ||||||
|  | 			std::get<3>(el)->setIcon(QIcon(":/linktype/discord")); | ||||||
|  | 			break; | ||||||
|  | 		case streamfx::ui::about::link_type::SOCIAL_TWITTER: | ||||||
|  | 			std::get<3>(el)->setIcon(QIcon(":/linktype/twitter")); | ||||||
|  | 			break; | ||||||
|  | 		case streamfx::ui::about::link_type::SOCIAL_FACEBOOK: | ||||||
|  | 			std::get<3>(el)->setIcon(QIcon(":/linktype/facebook")); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		std::get<3>(el)->setText(QString::fromUtf8(std::get<2>(el).c_str())); | ||||||
|  | 		std::get<4>(el).setUrl(QString::fromStdString(std::get<1>(el))); | ||||||
|  | 	} | ||||||
|  | 	connect(link1, &QPushButton::pressed, this, &streamfx::ui::about_entry::on_link1_clicked); | ||||||
|  | 	connect(link2, &QPushButton::pressed, this, &streamfx::ui::about_entry::on_link2_clicked); | ||||||
|  | 
 | ||||||
|  | 	// Don't free up space when hidden.
 | ||||||
|  | 	/*if (!(link1->isVisible() || link2->isVisible())) {
 | ||||||
|  | 		QSizePolicy qsp = link1->sizePolicy(); | ||||||
|  | 		qsp.setRetainSizeWhenHidden(true); | ||||||
|  | 		link1->setSizePolicy(qsp); | ||||||
|  | 	}*/ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | streamfx::ui::about_entry::~about_entry() {} | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::about_entry::on_link1_clicked() | ||||||
|  | { | ||||||
|  | 	// FIXME! Button clicks twice?
 | ||||||
|  | 	for (size_t attempt = 0; (attempt < 10) && (!QDesktopServices::openUrl(_link1_url)); attempt++) { | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::about_entry::on_link2_clicked() | ||||||
|  | { | ||||||
|  | 	for (size_t attempt = 0; (attempt < 10) && (!QDesktopServices::openUrl(_link2_url)); attempt++) { | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -0,0 +1,51 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2020 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include <chrono> | ||||||
|  | #include "ui-about.hpp" | ||||||
|  | #include "ui-common.hpp" | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(push) | ||||||
|  | #pragma warning(disable : 4251 4365 4371 4619 4946) | ||||||
|  | #endif | ||||||
|  | #include "ui_about-entry.h" | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(pop) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace streamfx::ui { | ||||||
|  | 	class about_entry : public QWidget, public Ui::AboutEntry { | ||||||
|  | 		Q_OBJECT | ||||||
|  | 
 | ||||||
|  | 		private: | ||||||
|  | 		std::chrono::high_resolution_clock::time_point _last_click; | ||||||
|  | 		QUrl                                           _link1_url; | ||||||
|  | 		QUrl                                           _link2_url; | ||||||
|  | 
 | ||||||
|  | 		public: | ||||||
|  | 		about_entry(QWidget* parent, ui::about::entry& entry); | ||||||
|  | 		~about_entry(); | ||||||
|  | 
 | ||||||
|  | 		public slots: | ||||||
|  | 		void on_link1_clicked(); | ||||||
|  | 		void on_link2_clicked(); | ||||||
|  | 	}; | ||||||
|  | } // namespace streamfx::ui
 | ||||||
|  | @ -0,0 +1,204 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2020 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "ui-about.hpp" | ||||||
|  | #include <obs-frontend-api.h> | ||||||
|  | #include <map> | ||||||
|  | #include "ui-about-entry.hpp" | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(push) | ||||||
|  | #pragma warning(disable : 4251 4365 4371 4619 4946) | ||||||
|  | #endif | ||||||
|  | #include <QLayout> | ||||||
|  | #include <QLayoutItem> | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(pop) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | constexpr std::string_view text_social_facebook = "Facebook"; | ||||||
|  | constexpr std::string_view text_social_twitch   = "Twitch"; | ||||||
|  | constexpr std::string_view text_social_twitter  = "Twitter"; | ||||||
|  | constexpr std::string_view text_social_youtube  = "YouTube"; | ||||||
|  | 
 | ||||||
|  | static const std::list<streamfx::ui::about::entry> _entries = { | ||||||
|  | 	// Contributers
 | ||||||
|  | 	streamfx::ui::about::entry{"Michael \"Xaymar\" Dirks", streamfx::ui::about::role_type::CONTRIBUTOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::GENERIC, "https://xaymar.com", "Blog & Stuff", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://twitch.tv/xaymar", | ||||||
|  | 							   text_social_twitch.data()}, | ||||||
|  | 
 | ||||||
|  | 	// Translators
 | ||||||
|  | 	streamfx::ui::about::entry{"FrozenNortherner", streamfx::ui::about::role_type::TRANSLATOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://twitch.tv/frozennortherner", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"HANAWINS", streamfx::ui::about::role_type::TRANSLATOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.twitch.tv/hanawins", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"hydargos", streamfx::ui::about::role_type::TRANSLATOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.youtube.com/hydargos", | ||||||
|  | 							   text_social_youtube.data(), streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Monsteer", streamfx::ui::about::role_type::TRANSLATOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITTER, "https://twitter.com/cooliguay", | ||||||
|  | 							   text_social_twitter.data(), streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Nanito", streamfx::ui::about::role_type::TRANSLATOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://twitch.tv/nanito", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::SOCIAL_FACEBOOK, | ||||||
|  | 							   "https://facebook.com/nanitotv", text_social_facebook.data()}, | ||||||
|  | 
 | ||||||
|  | 	// Separator
 | ||||||
|  | 	streamfx::ui::about::entry{"", streamfx::ui::about::role_type::NONE, "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   "", streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 
 | ||||||
|  | 	// Supporters
 | ||||||
|  | 	streamfx::ui::about::entry{"GranDroidTonight", streamfx::ui::about::role_type::PATREON_SUPPORTER, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.twitch.tv/GranDroidTonight", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::SOCIAL_YOUTUBE, | ||||||
|  | 							   "https://youtube.com/channel/UCGoT2XFPpeKaL1QuY_NPDuA", text_social_youtube.data()}, | ||||||
|  | 	streamfx::ui::about::entry{"chi11estpanda", streamfx::ui::about::role_type::PATREON_SUPPORTER, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.twitch.tv/chi11estpanda", | ||||||
|  | 							   "Gaming, Life and Laughs", streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"DandiDoesIt", streamfx::ui::about::role_type::PATREON_SUPPORTER, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.twitch.tv/DandiDoesIt", | ||||||
|  | 							   "Twitch Channel", streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"iamresist", streamfx::ui::about::role_type::PATREON_SUPPORTER, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://twitch.tv/iamresist", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Nordern", streamfx::ui::about::role_type::PATREON_SUPPORTER, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_YOUTUBE, "https://youtube.com/nordern", | ||||||
|  | 							   text_social_youtube.data(), streamfx::ui::about::link_type::SOCIAL_TWITCH, | ||||||
|  | 							   "https://www.twitch.tv/thenordern", text_social_twitch.data()}, | ||||||
|  | 
 | ||||||
|  | 	// Separator
 | ||||||
|  | 	streamfx::ui::about::entry{"", streamfx::ui::about::role_type::NONE, "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   "", streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 
 | ||||||
|  | 	// Family
 | ||||||
|  | 	streamfx::ui::about::entry{"Andrea Stenschke", streamfx::ui::about::role_type::FAMILY, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::NONE, "", "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Carsten Dirks", streamfx::ui::about::role_type::FAMILY, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::NONE, "", "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Christian \"Azekil\" Dirks", streamfx::ui::about::role_type::FAMILY, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::NONE, "", "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Gabriele Rantfl", streamfx::ui::about::role_type::FAMILY, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::NONE, "", "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   ""}, | ||||||
|  | 	streamfx::ui::about::entry{"Reiner Rantfl", streamfx::ui::about::role_type::FAMILY, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::NONE, "", "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   ""}, | ||||||
|  | 	streamfx::ui::about::entry{"René \"Dex\" Dirks", streamfx::ui::about::role_type::FAMILY, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://twitch.tv/vektordex", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::GENERIC, | ||||||
|  | 							   "https://worldofdex.de", "Website"}, | ||||||
|  | 
 | ||||||
|  | 	// Friends
 | ||||||
|  | 	streamfx::ui::about::entry{"Axelle", streamfx::ui::about::role_type::FRIEND, "Xaymar", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.twitch.tv/axelle123", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::SOCIAL_TWITTER, | ||||||
|  | 							   "https://twitter.com/AxellesNobody", text_social_twitter.data()}, | ||||||
|  | 
 | ||||||
|  | 	// Separator
 | ||||||
|  | 	streamfx::ui::about::entry{"", streamfx::ui::about::role_type::NONE, "", streamfx::ui::about::link_type::NONE, "", | ||||||
|  | 							   "", streamfx::ui::about::link_type::NONE, "", ""}, | ||||||
|  | 
 | ||||||
|  | 	// Creators
 | ||||||
|  | 	streamfx::ui::about::entry{"EposVox", streamfx::ui::about::role_type::CREATOR, "", | ||||||
|  | 							   streamfx::ui::about::link_type::SOCIAL_TWITCH, "https://www.twitch.tv/EposVox", | ||||||
|  | 							   text_social_twitch.data(), streamfx::ui::about::link_type::SOCIAL_YOUTUBE, | ||||||
|  | 							   "https://youtube.com/c/EposVox", text_social_youtube.data()}, | ||||||
|  | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | streamfx::ui::about::about() : QDialog(reinterpret_cast<QWidget*>(obs_frontend_get_main_window())) | ||||||
|  | { | ||||||
|  | 	setupUi(this); | ||||||
|  | 
 | ||||||
|  | 	// Remove some extra styling.
 | ||||||
|  | 	setWindowFlag(Qt::WindowContextHelpButtonHint, false); // Remove the unimplemented help button.
 | ||||||
|  | 
 | ||||||
|  | 	// Thank every single helper.
 | ||||||
|  | 	bool         column_selector = false; | ||||||
|  | 	size_t       row_selector    = 0; | ||||||
|  | 	QGridLayout* content_layout  = dynamic_cast<QGridLayout*>(content->layout()); | ||||||
|  | 	for (auto entry : _entries) { | ||||||
|  | 		if (entry.name.size() == 0) { | ||||||
|  | 			row_selector += 2; | ||||||
|  | 			column_selector = 0; | ||||||
|  | 
 | ||||||
|  | 			// Add a separator line.
 | ||||||
|  | 			auto separator = new QFrame(content); | ||||||
|  | 			{ | ||||||
|  | 				auto sp = separator->sizePolicy(); | ||||||
|  | 				sp.setVerticalPolicy(QSizePolicy::Fixed); | ||||||
|  | 				separator->setSizePolicy(sp); | ||||||
|  | 			} | ||||||
|  | 			separator->setFrameShape(QFrame::HLine); | ||||||
|  | 			separator->setFrameShadow(QFrame::Sunken); | ||||||
|  | 			separator->setMaximumHeight(1); | ||||||
|  | 			separator->setMinimumHeight(1); | ||||||
|  | 			separator->setFixedHeight(1); | ||||||
|  | 			separator->setLineWidth(1); | ||||||
|  | 			content_layout->addWidget(separator, static_cast<int>(row_selector - 1), 0, 1, 2); | ||||||
|  | 			content_layout->setRowStretch(static_cast<int>(row_selector - 1), 0); | ||||||
|  | 		} else { | ||||||
|  | 			streamfx::ui::about_entry* v = new streamfx::ui::about_entry(content, entry); | ||||||
|  | 
 | ||||||
|  | 			content_layout->addWidget(v, static_cast<int>(row_selector), column_selector ? 1 : 0); | ||||||
|  | 			content_layout->setRowStretch(static_cast<int>(row_selector), 0); | ||||||
|  | 
 | ||||||
|  | 			if (column_selector) { | ||||||
|  | 				row_selector++; | ||||||
|  | 			} | ||||||
|  | 			column_selector = !column_selector; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	{ | ||||||
|  | 		row_selector++; | ||||||
|  | 		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); | ||||||
|  | 		content_layout->addWidget(padder, static_cast<int>(row_selector), 0, 1, 2); | ||||||
|  | 		content_layout->setRowStretch(static_cast<int>(row_selector), 9999); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	content_layout->setColumnStretch(0, 1); | ||||||
|  | 	content_layout->setColumnStretch(1, 1); | ||||||
|  | 	content->setSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Maximum); | ||||||
|  | 
 | ||||||
|  | 	// 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(); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,83 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2020 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "common.hpp" | ||||||
|  | #include "ui-common.hpp" | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(push) | ||||||
|  | #pragma warning(disable : 4251 4365 4371 4619 4946) | ||||||
|  | #endif | ||||||
|  | #include "ui_about.h" | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(pop) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace streamfx::ui { | ||||||
|  | 	class about : public QDialog, public Ui::About { | ||||||
|  | 		Q_OBJECT | ||||||
|  | 
 | ||||||
|  | 		public: | ||||||
|  | 		enum class role_type : std::int32_t { | ||||||
|  | 			NONE, | ||||||
|  | 			CONTRIBUTOR, | ||||||
|  | 			TRANSLATOR, | ||||||
|  | 			FAMILY, | ||||||
|  | 			FRIEND, | ||||||
|  | 			PATREON_SUPPORTER, | ||||||
|  | 			GITHUB_SUPPORTER, | ||||||
|  | 			TWITCH_SUPPORTER, | ||||||
|  | 			CREATOR, | ||||||
|  | 		}; | ||||||
|  | 		enum class link_type : std::int32_t { | ||||||
|  | 			NONE, | ||||||
|  | 			GENERIC, | ||||||
|  | 
 | ||||||
|  | 			// Social Links
 | ||||||
|  | 			SOCIAL_TWITCH = 2000, | ||||||
|  | 			SOCIAL_YOUTUBE, | ||||||
|  | 			SOCIAL_DISCORD, | ||||||
|  | 			SOCIAL_TWITTER, | ||||||
|  | 			SOCIAL_FACEBOOK, | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		struct entry { | ||||||
|  | 			std::string          name; | ||||||
|  | 			ui::about::role_type role; | ||||||
|  | 			std::string          role_custom; | ||||||
|  | 
 | ||||||
|  | 			ui::about::link_type link1_type; | ||||||
|  | 			std::string          link1_address; | ||||||
|  | 			std::string          link1_text; | ||||||
|  | 
 | ||||||
|  | 			ui::about::link_type link2_type; | ||||||
|  | 			std::string          link2_address; | ||||||
|  | 			std::string          link2_text; | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		public: | ||||||
|  | 		about(); | ||||||
|  | 		~about(); | ||||||
|  | 
 | ||||||
|  | 		public slots: | ||||||
|  | 		; // Not having this breaks some linters.
 | ||||||
|  | 		void on_ok(); | ||||||
|  | 	}; | ||||||
|  | } // namespace streamfx::ui
 | ||||||
|  | @ -0,0 +1,43 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2017 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(push) | ||||||
|  | #pragma warning(disable : 4251 4365 4371 4619 4946) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include <QAction> | ||||||
|  | #include <QDesktopServices> | ||||||
|  | #include <QDialog> | ||||||
|  | #include <QMenu> | ||||||
|  | #include <QObject> | ||||||
|  | #include <QString> | ||||||
|  | #include <QTranslator> | ||||||
|  | #include <QUrl> | ||||||
|  | #include <QWidget> | ||||||
|  | 
 | ||||||
|  | extern "C" { | ||||||
|  | #include <obs-frontend-api.h> | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #pragma warning(pop) | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,200 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2020 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "ui.hpp" | ||||||
|  | #include "common.hpp" | ||||||
|  | #include "strings.hpp" | ||||||
|  | #include <string_view> | ||||||
|  | #include "configuration.hpp" | ||||||
|  | #include "obs/obs-tools.hpp" | ||||||
|  | #include "plugin.hpp" | ||||||
|  | 
 | ||||||
|  | #include <obs-frontend-api.h> | ||||||
|  | 
 | ||||||
|  | // Translation Keys
 | ||||||
|  | constexpr std::string_view _i18n_prefix            = "StreamFX::"; | ||||||
|  | constexpr std::string_view _i18n_menu              = "UI.Menu"; | ||||||
|  | constexpr std::string_view _i18n_menu_report_issue = "UI.Menu.ReportIssue"; | ||||||
|  | constexpr std::string_view _i18n_menu_request_help = "UI.Menu.RequestHelp"; | ||||||
|  | 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_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_report_issue = "https://github.com/Xaymar/obs-StreamFX/issues/new?template=issue.md"; | ||||||
|  | constexpr std::string_view _url_request_help = "https://github.com/Xaymar/obs-StreamFX/issues/new?template=help.md"; | ||||||
|  | constexpr std::string_view _url_website      = "https://streamfx.xaymar.com"; | ||||||
|  | constexpr std::string_view _url_discord      = "https://discordapp.com/invite/DaeJg7M"; | ||||||
|  | constexpr std::string_view _url_github       = "https://github.com/Xaymar/obs-StreamFX"; | ||||||
|  | 
 | ||||||
|  | bool streamfx::ui::handler::have_shown_about_streamfx(bool shown) | ||||||
|  | { | ||||||
|  | 	if (shown) { | ||||||
|  | 		obs_data_set_bool(streamfx::configuration::instance()->get().get(), _cfg_have_shown_about.data(), true); | ||||||
|  | 	} | ||||||
|  | 	if (streamfx::configuration::instance()->is_different_version()) { | ||||||
|  | 		return false; | ||||||
|  | 	} else { | ||||||
|  | 		return obs_data_get_bool(streamfx::configuration::instance()->get().get(), _cfg_have_shown_about.data()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | streamfx::ui::handler::handler() | ||||||
|  | 	: QObject(), _menu_action(), _menu(), | ||||||
|  | 
 | ||||||
|  | 	  _report_issue(), _request_help(), | ||||||
|  | 
 | ||||||
|  | 	  _link_website(), _link_discord(), _link_github(), | ||||||
|  | 
 | ||||||
|  | 	  _about_action(), _about_dialog() | ||||||
|  | { | ||||||
|  | 	// Qt Resources and Translators
 | ||||||
|  | 	Q_INIT_RESOURCE(streamfx); | ||||||
|  | 	QCoreApplication::installTranslator(new streamfx::ui::translator(this)); | ||||||
|  | 
 | ||||||
|  | 	// Handle all frontend events.
 | ||||||
|  | 	obs_frontend_add_event_callback(frontend_event_handler, this); | ||||||
|  | 
 | ||||||
|  | 	{ // Build StreamFX menu.
 | ||||||
|  | 		_menu = new QMenu(reinterpret_cast<QWidget*>(obs_frontend_get_main_window())); | ||||||
|  | 
 | ||||||
|  | 		{ // Github Issues
 | ||||||
|  | 			//_menu->addSeparator();
 | ||||||
|  | 			_report_issue = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_report_issue.data()))); | ||||||
|  | 			_request_help = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_request_help.data()))); | ||||||
|  | 			connect(_report_issue, &QAction::triggered, this, &streamfx::ui::handler::on_action_report_issue); | ||||||
|  | 			connect(_request_help, &QAction::triggered, this, &streamfx::ui::handler::on_action_request_help); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		{ // Official Links
 | ||||||
|  | 			_menu->addSeparator(); | ||||||
|  | 			_link_website = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_website.data()))); | ||||||
|  | 			_link_discord = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_discord.data()))); | ||||||
|  | 			_link_github  = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_github.data()))); | ||||||
|  | 			connect(_link_website, &QAction::triggered, this, &streamfx::ui::handler::on_action_website); | ||||||
|  | 			connect(_link_discord, &QAction::triggered, this, &streamfx::ui::handler::on_action_discord); | ||||||
|  | 			connect(_link_github, &QAction::triggered, this, &streamfx::ui::handler::on_action_github); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		{ // About StreamFX
 | ||||||
|  | 			_about_dialog = new streamfx::ui::about(); | ||||||
|  | 			_menu->addSeparator(); | ||||||
|  | 			_about_action = _menu->addAction(QString::fromUtf8(D_TRANSLATE(_i18n_menu_about.data()))); | ||||||
|  | 			connect(_about_action, &QAction::triggered, this, &streamfx::ui::handler::on_action_about); | ||||||
|  | 			if (!have_shown_about_streamfx()) { | ||||||
|  | 				// Automatically show it if it has not yet been shown.
 | ||||||
|  | 				_about_dialog->show(); | ||||||
|  | 				have_shown_about_streamfx(true); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | streamfx::ui::handler::~handler() | ||||||
|  | { | ||||||
|  | 	// Handle all frontend events.
 | ||||||
|  | 	obs_frontend_remove_event_callback(frontend_event_handler, this); | ||||||
|  | 
 | ||||||
|  | 	// Qt Resources and Translators
 | ||||||
|  | 	Q_CLEANUP_RESOURCE(streamfx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::handler::on_obs_loaded() | ||||||
|  | { | ||||||
|  | 	// Add StreamFX menu.
 | ||||||
|  | 	_menu_action = reinterpret_cast<QAction*>(obs_frontend_add_tools_menu_qaction(D_TRANSLATE(_i18n_menu.data()))); | ||||||
|  | 	_menu_action->setMenu(_menu); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::handler::on_action_report_issue(bool) | ||||||
|  | { | ||||||
|  | 	QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_report_issue.data()))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::handler::on_action_request_help(bool) | ||||||
|  | { | ||||||
|  | 	QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_request_help.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_github(bool) | ||||||
|  | { | ||||||
|  | 	QDesktopServices::openUrl(QUrl(QString::fromUtf8(_url_github.data()))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::handler::on_action_about(bool checked) | ||||||
|  | { | ||||||
|  | 	_about_dialog->show(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static std::shared_ptr<streamfx::ui::handler> _handler_singleton; | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::handler::initialize() | ||||||
|  | { | ||||||
|  | 	_handler_singleton = std::make_shared<streamfx::ui::handler>(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void streamfx::ui::handler::finalize() | ||||||
|  | { | ||||||
|  | 	_handler_singleton.reset(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::shared_ptr<streamfx::ui::handler> streamfx::ui::handler::get() | ||||||
|  | { | ||||||
|  | 	return _handler_singleton; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | { | ||||||
|  | 	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())); | ||||||
|  | 	} | ||||||
|  | 	return QString(); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,82 @@ | ||||||
|  | /*
 | ||||||
|  |  * Modern effects for a modern Streamer | ||||||
|  |  * Copyright (C) 2020 Michael Fabian Dirks | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | #include "ui-about.hpp" | ||||||
|  | #include "ui-common.hpp" | ||||||
|  | 
 | ||||||
|  | namespace streamfx::ui { | ||||||
|  | 	class handler : public QObject { | ||||||
|  | 		Q_OBJECT | ||||||
|  | 
 | ||||||
|  | 		private: | ||||||
|  | 		QAction* _menu_action; | ||||||
|  | 		QMenu*   _menu; | ||||||
|  | 
 | ||||||
|  | 		// Bug Report, Help Request
 | ||||||
|  | 		QAction* _report_issue; | ||||||
|  | 		QAction* _request_help; | ||||||
|  | 
 | ||||||
|  | 		// Website, Discord, Source
 | ||||||
|  | 		QAction* _link_website; | ||||||
|  | 		QAction* _link_discord; | ||||||
|  | 		QAction* _link_github; | ||||||
|  | 
 | ||||||
|  | 		// About Dialog
 | ||||||
|  | 		QAction*   _about_action; | ||||||
|  | 		ui::about* _about_dialog; | ||||||
|  | 
 | ||||||
|  | 		public: | ||||||
|  | 		handler(); | ||||||
|  | 		~handler(); | ||||||
|  | 
 | ||||||
|  | 		bool have_shown_about_streamfx(bool shown = false); | ||||||
|  | 
 | ||||||
|  | 		private: | ||||||
|  | 		static void frontend_event_handler(obs_frontend_event event, void* private_data); | ||||||
|  | 
 | ||||||
|  | 		void on_obs_loaded(); | ||||||
|  | 
 | ||||||
|  | 		public slots: | ||||||
|  | 		; // Not having this breaks some linters.
 | ||||||
|  | 		void on_action_report_issue(bool); | ||||||
|  | 		void on_action_request_help(bool); | ||||||
|  | 		void on_action_website(bool); | ||||||
|  | 		void on_action_discord(bool); | ||||||
|  | 		void on_action_github(bool); | ||||||
|  | 		void on_action_about(bool); | ||||||
|  | 
 | ||||||
|  | 		public /* Singleton */: | ||||||
|  | 		static void initialize(); | ||||||
|  | 
 | ||||||
|  | 		static void finalize(); | ||||||
|  | 
 | ||||||
|  | 		static std::shared_ptr<ui::handler> get(); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class translator : public QTranslator { | ||||||
|  | 		public: | ||||||
|  | 		translator(QObject* parent = nullptr); | ||||||
|  | 		~translator(); | ||||||
|  | 
 | ||||||
|  | 		virtual QString translate(const char* context, const char* sourceText, const char* disambiguation = nullptr, | ||||||
|  | 								  int n = -1) const override; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | } // namespace streamfx::ui
 | ||||||
|  | @ -0,0 +1,243 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>AboutEntry</class> | ||||||
|  |  <widget class="QWidget" name="AboutEntry"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>195</width> | ||||||
|  |     <height>99</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="sizePolicy"> | ||||||
|  |    <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> | ||||||
|  |     <horstretch>0</horstretch> | ||||||
|  |     <verstretch>0</verstretch> | ||||||
|  |    </sizepolicy> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string>Form</string> | ||||||
|  |   </property> | ||||||
|  |   <property name="styleSheet"> | ||||||
|  |    <string notr="true">QWidget { | ||||||
|  | 	background: hsl(0, 0%, 20%); | ||||||
|  | 	color: hsl(0, 0%, 100%); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QPushButton, | ||||||
|  | QPushButton:default, | ||||||
|  | QPushButton:flat, | ||||||
|  | QPushButton:checked { | ||||||
|  | 	background: hsl(0, 0%, 25%); | ||||||
|  | 	border: 0; | ||||||
|  | 	padding: 5px; | ||||||
|  | 	box-shadow: 0; | ||||||
|  | 	cursor: pointer; | ||||||
|  | 	padding: 4px; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QWidget[objectName="title"] { | ||||||
|  | 	color: hsl(200, 100%, 80%); | ||||||
|  | }</string> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |    <property name="spacing"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="leftMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="topMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="rightMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="bottomMargin"> | ||||||
|  |     <number>0</number> | ||||||
|  |    </property> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QFrame" name="frame"> | ||||||
|  |      <property name="sizePolicy"> | ||||||
|  |       <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> | ||||||
|  |        <horstretch>0</horstretch> | ||||||
|  |        <verstretch>0</verstretch> | ||||||
|  |       </sizepolicy> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShape"> | ||||||
|  |       <enum>QFrame::StyledPanel</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="frameShadow"> | ||||||
|  |       <enum>QFrame::Raised</enum> | ||||||
|  |      </property> | ||||||
|  |      <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||||
|  |       <property name="spacing"> | ||||||
|  |        <number>5</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="leftMargin"> | ||||||
|  |        <number>5</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="topMargin"> | ||||||
|  |        <number>5</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="rightMargin"> | ||||||
|  |        <number>5</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="bottomMargin"> | ||||||
|  |        <number>5</number> | ||||||
|  |       </property> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="name"> | ||||||
|  |         <property name="sizePolicy"> | ||||||
|  |          <sizepolicy hsizetype="Ignored" vsizetype="Maximum"> | ||||||
|  |           <horstretch>0</horstretch> | ||||||
|  |           <verstretch>0</verstretch> | ||||||
|  |          </sizepolicy> | ||||||
|  |         </property> | ||||||
|  |         <property name="font"> | ||||||
|  |          <font> | ||||||
|  |           <pointsize>14</pointsize> | ||||||
|  |           <weight>75</weight> | ||||||
|  |           <bold>true</bold> | ||||||
|  |           <stylestrategy>PreferAntialias</stylestrategy> | ||||||
|  |          </font> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string notr="true">Supporter Name</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <widget class="QLabel" name="title"> | ||||||
|  |         <property name="sizePolicy"> | ||||||
|  |          <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> | ||||||
|  |           <horstretch>0</horstretch> | ||||||
|  |           <verstretch>0</verstretch> | ||||||
|  |          </sizepolicy> | ||||||
|  |         </property> | ||||||
|  |         <property name="font"> | ||||||
|  |          <font> | ||||||
|  |           <pointsize>10</pointsize> | ||||||
|  |          </font> | ||||||
|  |         </property> | ||||||
|  |         <property name="text"> | ||||||
|  |          <string notr="true">Supporter Title</string> | ||||||
|  |         </property> | ||||||
|  |         <property name="alignment"> | ||||||
|  |          <set>Qt::AlignCenter</set> | ||||||
|  |         </property> | ||||||
|  |        </widget> | ||||||
|  |       </item> | ||||||
|  |       <item> | ||||||
|  |        <layout class="QHBoxLayout" name="linkLayout"> | ||||||
|  |         <property name="spacing"> | ||||||
|  |          <number>5</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="leftMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="topMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="rightMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="bottomMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QPushButton" name="link1"> | ||||||
|  |           <property name="minimumSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>0</width> | ||||||
|  |             <height>36</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="maximumSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>16777215</width> | ||||||
|  |             <height>36</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="baseSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>0</width> | ||||||
|  |             <height>36</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="font"> | ||||||
|  |            <font> | ||||||
|  |             <pointsize>10</pointsize> | ||||||
|  |            </font> | ||||||
|  |           </property> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string notr="true">YouTube</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="icon"> | ||||||
|  |            <iconset> | ||||||
|  |             <normaloff>:/social/youtube</normaloff>:/social/youtube</iconset> | ||||||
|  |           </property> | ||||||
|  |           <property name="iconSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>32</width> | ||||||
|  |             <height>32</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |         <item> | ||||||
|  |          <widget class="QPushButton" name="link2"> | ||||||
|  |           <property name="minimumSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>0</width> | ||||||
|  |             <height>36</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="maximumSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>16777215</width> | ||||||
|  |             <height>36</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="baseSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>0</width> | ||||||
|  |             <height>36</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |           <property name="font"> | ||||||
|  |            <font> | ||||||
|  |             <pointsize>10</pointsize> | ||||||
|  |            </font> | ||||||
|  |           </property> | ||||||
|  |           <property name="text"> | ||||||
|  |            <string notr="true">Twitch</string> | ||||||
|  |           </property> | ||||||
|  |           <property name="icon"> | ||||||
|  |            <iconset> | ||||||
|  |             <normaloff>:/social/twitch</normaloff>:/social/twitch</iconset> | ||||||
|  |           </property> | ||||||
|  |           <property name="iconSize"> | ||||||
|  |            <size> | ||||||
|  |             <width>32</width> | ||||||
|  |             <height>32</height> | ||||||
|  |            </size> | ||||||
|  |           </property> | ||||||
|  |          </widget> | ||||||
|  |         </item> | ||||||
|  |        </layout> | ||||||
|  |       </item> | ||||||
|  |      </layout> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources> | ||||||
|  |   <include location="streamfx.qrc"/> | ||||||
|  |  </resources> | ||||||
|  |  <connections/> | ||||||
|  | </ui> | ||||||
|  | @ -0,0 +1,173 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <ui version="4.0"> | ||||||
|  |  <class>About</class> | ||||||
|  |  <widget class="QWidget" name="About"> | ||||||
|  |   <property name="geometry"> | ||||||
|  |    <rect> | ||||||
|  |     <x>0</x> | ||||||
|  |     <y>0</y> | ||||||
|  |     <width>512</width> | ||||||
|  |     <height>512</height> | ||||||
|  |    </rect> | ||||||
|  |   </property> | ||||||
|  |   <property name="minimumSize"> | ||||||
|  |    <size> | ||||||
|  |     <width>512</width> | ||||||
|  |     <height>512</height> | ||||||
|  |    </size> | ||||||
|  |   </property> | ||||||
|  |   <property name="maximumSize"> | ||||||
|  |    <size> | ||||||
|  |     <width>1024</width> | ||||||
|  |     <height>16777215</height> | ||||||
|  |    </size> | ||||||
|  |   </property> | ||||||
|  |   <property name="windowTitle"> | ||||||
|  |    <string comment="StreamFX::UI.About.Title">StreamFX::UI.About.Title</string> | ||||||
|  |   </property> | ||||||
|  |   <property name="styleSheet"> | ||||||
|  |    <string notr="true">QWidget { | ||||||
|  | 	background: transparent; | ||||||
|  | 	color: hsl(0, 0%, 100%); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QWidget[objectName="About"], | ||||||
|  | QWidget[objectName="contentContainer"], | ||||||
|  | QWidget[objectName="content"] { | ||||||
|  | 	background: hsl(0, 0%, 10%); | ||||||
|  | 	border: 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | QWidget[objectName="PaddleMeDaddy"] { | ||||||
|  | 	background: hsl(0, 0%, 10%); | ||||||
|  | }</string> | ||||||
|  |   </property> | ||||||
|  |   <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |    <property name="spacing"> | ||||||
|  |     <number>5</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="leftMargin"> | ||||||
|  |     <number>10</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="topMargin"> | ||||||
|  |     <number>10</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="rightMargin"> | ||||||
|  |     <number>10</number> | ||||||
|  |    </property> | ||||||
|  |    <property name="bottomMargin"> | ||||||
|  |     <number>10</number> | ||||||
|  |    </property> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QLabel" name="logo"> | ||||||
|  |      <property name="sizePolicy"> | ||||||
|  |       <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||||||
|  |        <horstretch>0</horstretch> | ||||||
|  |        <verstretch>0</verstretch> | ||||||
|  |       </sizepolicy> | ||||||
|  |      </property> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string/> | ||||||
|  |      </property> | ||||||
|  |      <property name="pixmap"> | ||||||
|  |       <pixmap resource="streamfx.qrc">:/logos/streamfx_logo</pixmap> | ||||||
|  |      </property> | ||||||
|  |      <property name="scaledContents"> | ||||||
|  |       <bool>false</bool> | ||||||
|  |      </property> | ||||||
|  |      <property name="alignment"> | ||||||
|  |       <set>Qt::AlignCenter</set> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QLabel" name="text"> | ||||||
|  |      <property name="sizePolicy"> | ||||||
|  |       <sizepolicy hsizetype="Expanding" vsizetype="Maximum"> | ||||||
|  |        <horstretch>0</horstretch> | ||||||
|  |        <verstretch>0</verstretch> | ||||||
|  |       </sizepolicy> | ||||||
|  |      </property> | ||||||
|  |      <property name="font"> | ||||||
|  |       <font> | ||||||
|  |        <pointsize>12</pointsize> | ||||||
|  |        <kerning>true</kerning> | ||||||
|  |       </font> | ||||||
|  |      </property> | ||||||
|  |      <property name="text"> | ||||||
|  |       <string comment="StreamFX::UI.About.Text"><html><head/><body><p>StreamFX is made possible by all the supporters on <a href="https://patreon.com/Xaymar"><span style=" text-decoration: underline; color:#00aaff;">Patreon</span></a>, on <a href="https://github.com/sponsors/xaymar"><span style=" text-decoration: underline; color:#00aaff;">Github Sponsors</span></a>, and anyone donating through <a href="https://paypal.me/Xaymar"><span style=" text-decoration: underline; color:#00aaff;">PayPal</span></a>. Additional thanks go out to all the translators helping out with the localization on <a href="https://crowdin.com/project/obs-stream-effects"><span style=" text-decoration: underline; color:#00aaff;">Crowdin</span></a>. You all are amazing!</p></body></html></string> | ||||||
|  |      </property> | ||||||
|  |      <property name="alignment"> | ||||||
|  |       <set>Qt::AlignHCenter|Qt::AlignTop</set> | ||||||
|  |      </property> | ||||||
|  |      <property name="wordWrap"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |      </property> | ||||||
|  |      <property name="openExternalLinks"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |      </property> | ||||||
|  |      <property name="textInteractionFlags"> | ||||||
|  |       <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QScrollArea" name="contentContainer"> | ||||||
|  |      <property name="horizontalScrollBarPolicy"> | ||||||
|  |       <enum>Qt::ScrollBarAlwaysOff</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="widgetResizable"> | ||||||
|  |       <bool>true</bool> | ||||||
|  |      </property> | ||||||
|  |      <property name="alignment"> | ||||||
|  |       <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> | ||||||
|  |      </property> | ||||||
|  |      <widget class="QWidget" name="content"> | ||||||
|  |       <property name="geometry"> | ||||||
|  |        <rect> | ||||||
|  |         <x>0</x> | ||||||
|  |         <y>0</y> | ||||||
|  |         <width>492</width> | ||||||
|  |         <height>282</height> | ||||||
|  |        </rect> | ||||||
|  |       </property> | ||||||
|  |       <layout class="QGridLayout" name="gridLayout"> | ||||||
|  |        <property name="leftMargin"> | ||||||
|  |         <number>0</number> | ||||||
|  |        </property> | ||||||
|  |        <property name="topMargin"> | ||||||
|  |         <number>0</number> | ||||||
|  |        </property> | ||||||
|  |        <property name="rightMargin"> | ||||||
|  |         <number>0</number> | ||||||
|  |        </property> | ||||||
|  |        <property name="bottomMargin"> | ||||||
|  |         <number>0</number> | ||||||
|  |        </property> | ||||||
|  |        <property name="spacing"> | ||||||
|  |         <number>5</number> | ||||||
|  |        </property> | ||||||
|  |       </layout> | ||||||
|  |      </widget> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |    <item> | ||||||
|  |     <widget class="QDialogButtonBox" name="buttonBox"> | ||||||
|  |      <property name="orientation"> | ||||||
|  |       <enum>Qt::Horizontal</enum> | ||||||
|  |      </property> | ||||||
|  |      <property name="standardButtons"> | ||||||
|  |       <set>QDialogButtonBox::Ok</set> | ||||||
|  |      </property> | ||||||
|  |      <property name="centerButtons"> | ||||||
|  |       <bool>false</bool> | ||||||
|  |      </property> | ||||||
|  |     </widget> | ||||||
|  |    </item> | ||||||
|  |   </layout> | ||||||
|  |  </widget> | ||||||
|  |  <resources> | ||||||
|  |   <include location="streamfx.qrc"/> | ||||||
|  |  </resources> | ||||||
|  |  <connections/> | ||||||
|  | </ui> | ||||||
| After Width: | Height: | Size: 613 B | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 937 B | 
| After Width: | Height: | Size: 656 B | 
| After Width: | Height: | Size: 727 B | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 58 KiB | 
|  | @ -0,0 +1,13 @@ | ||||||
|  | <RCC> | ||||||
|  |   <qresource prefix="logos"> | ||||||
|  |     <file alias="streamfx_logo">logo.png</file> | ||||||
|  |   </qresource> | ||||||
|  |   <qresource prefix="linktype"> | ||||||
|  |     <file alias="discord">linktype_discord.png</file> | ||||||
|  |     <file alias="facebook">linktype_facebook.png</file> | ||||||
|  |     <file alias="generic">linktype_generic.png</file> | ||||||
|  |     <file alias="twitch">linktype_twitch.png</file> | ||||||
|  |     <file alias="twitter">linktype_twitter.png</file> | ||||||
|  |     <file alias="youtube">linktype_youtube.png</file> | ||||||
|  |   </qresource> | ||||||
|  | </RCC> | ||||||