diff --git a/src/lang/english.txt b/src/lang/english.txt index 35600f0b19..8eb614bf9e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -943,6 +943,8 @@ STR_GAME_OPTIONS_TAB_GRAPHICS :Graphics STR_GAME_OPTIONS_TAB_GRAPHICS_TT :{BLACK}Choose graphics settings STR_GAME_OPTIONS_TAB_SOUND :Sound STR_GAME_OPTIONS_TAB_SOUND_TT :{BLACK}Choose sound and music settings +STR_GAME_OPTIONS_TAB_SOCIAL :Social +STR_GAME_OPTIONS_TAB_SOCIAL_TT :{BLACK}Choose social integration settings STR_GAME_OPTIONS_VOLUME :Volume STR_GAME_OPTIONS_SFX_VOLUME :Sound effects @@ -1082,6 +1084,19 @@ STR_GAME_OPTIONS_BASE_MUSIC :{BLACK}Base mus STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP :{BLACK}Select the base music set to use STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP :{BLACK}Additional information about the base music set +STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE :{LTBLUE}(no plugins to integrate with social platforms installed) + +STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE :{BLACK}{RAW_STRING} ({RAW_STRING}) +STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM :{BLACK}Platform: +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE :{BLACK}Plugin state: + +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_RUNNING :{GREEN}Running +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED :{RED}Failed to initialize +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_PLATFORM_NOT_RUNNING :{ORANGE}{RAW_STRING} not running +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNLOADED :{RED}Unloaded +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_DUPLICATE :{RED}Duplicated plugin +STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNSUPPORTED_API :{RED}Unsupported version + STR_BASESET_STATUS :{RAW_STRING} {RED}({NUM} missing/corrupted file{P "" s}) STR_ERROR_RESOLUTION_LIST_FAILED :{WHITE}Failed to retrieve a list of supported resolutions diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 15519b62f5..4e96a1e7ec 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -46,6 +46,7 @@ #include "network/network_gui.h" #include "network/network_survey.h" #include "video/video_driver.hpp" +#include "social_integration.h" #include "safeguards.h" @@ -170,6 +171,183 @@ static const std::map _volume_labels = { { 127, STR_GAME_OPTIONS_VOLUME_100 }, }; +static const NWidgetPart _nested_social_plugins_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_FRAME, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_TITLE), SetDataTip(STR_JUST_STRING2, STR_NULL), + NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGIN_PLATFORM, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_PLATFORM), SetMinimalSize(100, 12), SetDataTip(STR_JUST_RAW_STRING, STR_NULL), SetAlignment(SA_RIGHT), + EndContainer(), + NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE, STR_NULL), + NWidget(WWT_TEXT, COLOUR_GREY, WID_GO_SOCIAL_PLUGIN_STATE), SetMinimalSize(100, 12), SetDataTip(STR_JUST_STRING1, STR_NULL), SetAlignment(SA_RIGHT), + EndContainer(), + EndContainer(), + EndContainer(), +}; + +static const NWidgetPart _nested_social_plugins_none_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_TEXT, COLOUR_GREY), SetMinimalSize(0, 12), SetFill(1, 0), SetDataTip(STR_GAME_OPTIONS_SOCIAL_PLUGINS_NONE, STR_NULL), + EndContainer(), +}; + +class NWidgetSocialPlugins : public NWidgetVertical { +public: + NWidgetSocialPlugins() + { + this->plugins = SocialIntegration::GetPlugins(); + + if (this->plugins.empty()) { + auto widget = MakeNWidgets(std::begin(_nested_social_plugins_none_widgets), std::end(_nested_social_plugins_none_widgets), nullptr); + this->Add(std::move(widget)); + } else { + for (size_t i = 0; i < this->plugins.size(); i++) { + auto widget = MakeNWidgets(std::begin(_nested_social_plugins_widgets), std::end(_nested_social_plugins_widgets), nullptr); + this->Add(std::move(widget)); + } + } + + this->SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0); + } + + void FillWidgetLookup(WidgetLookup &widget_lookup) override + { + widget_lookup[WID_GO_SOCIAL_PLUGINS] = this; + NWidgetVertical::FillWidgetLookup(widget_lookup); + } + + void SetupSmallestSize(Window *w) override + { + this->current_index = -1; + NWidgetVertical::SetupSmallestSize(w); + } + + /** + * Find of all the plugins the one where the member is the widest (in pixels). + * + * @param member The member to check with. + * @return The plugin that has the widest value (in pixels) for the given member. + */ + template + std::string &GetWidestPlugin(T SocialIntegrationPlugin::*member) const + { + std::string *longest = &(this->plugins[0]->*member); + int longest_length = 0; + + for (auto *plugin : this->plugins) { + int length = GetStringBoundingBox(plugin->*member).width; + if (length > longest_length) { + longest_length = length; + longest = &(plugin->*member); + } + } + + return *longest; + } + + void SetStringParameters(int widget) const + { + switch (widget) { + case WID_GO_SOCIAL_PLUGIN_TITLE: + /* For SetupSmallestSize, use the longest string we have. */ + if (this->current_index < 0) { + SetDParamStr(0, GetWidestPlugin(&SocialIntegrationPlugin::name)); + SetDParamStr(1, GetWidestPlugin(&SocialIntegrationPlugin::version)); + break; + } + + if (this->plugins[this->current_index]->name.empty()) { + SetDParam(0, STR_JUST_RAW_STRING); + SetDParamStr(1, this->plugins[this->current_index]->basepath); + } else { + SetDParam(0, STR_GAME_OPTIONS_SOCIAL_PLUGIN_TITLE); + SetDParamStr(1, this->plugins[this->current_index]->name); + SetDParamStr(2, this->plugins[this->current_index]->version); + } + break; + + case WID_GO_SOCIAL_PLUGIN_PLATFORM: + /* For SetupSmallestSize, use the longest string we have. */ + if (this->current_index < 0) { + SetDParamStr(0, GetWidestPlugin(&SocialIntegrationPlugin::social_platform)); + break; + } + + SetDParamStr(0, this->plugins[this->current_index]->social_platform); + break; + + case WID_GO_SOCIAL_PLUGIN_STATE: { + static const std::pair state_to_string[] = { + { SocialIntegrationPlugin::RUNNING, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_RUNNING }, + { SocialIntegrationPlugin::FAILED, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED }, + { SocialIntegrationPlugin::PLATFORM_NOT_RUNNING, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_PLATFORM_NOT_RUNNING }, + { SocialIntegrationPlugin::UNLOADED, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNLOADED }, + { SocialIntegrationPlugin::DUPLICATE, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_DUPLICATE }, + { SocialIntegrationPlugin::UNSUPPORTED_API, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_UNSUPPORTED_API }, + }; + + /* For SetupSmallestSize, use the longest string we have. */ + if (this->current_index < 0) { + auto longest_plugin = GetWidestPlugin(&SocialIntegrationPlugin::social_platform); + + /* Set the longest plugin when looking for the longest status. */ + SetDParamStr(0, longest_plugin); + + StringID longest = STR_NULL; + int longest_length = 0; + for (auto state : state_to_string) { + int length = GetStringBoundingBox(state.second).width; + if (length > longest_length) { + longest_length = length; + longest = state.second; + } + } + + SetDParam(0, longest); + SetDParamStr(1, longest_plugin); + break; + } + + auto plugin = this->plugins[this->current_index]; + + /* Default string, in case no state matches. */ + SetDParam(0, STR_GAME_OPTIONS_SOCIAL_PLUGIN_STATE_FAILED); + SetDParamStr(1, plugin->social_platform); + + /* Find the string for the state. */ + for (auto state : state_to_string) { + if (plugin->state == state.first) { + SetDParam(0, state.second); + break; + } + } + } + break; + } + } + + void Draw(const Window *w) override + { + this->current_index = 0; + + for (auto &wid : this->children) { + wid->Draw(w); + this->current_index++; + } + } + +private: + int current_index = -1; + std::vector plugins; +}; + +/** Construct nested container widget for managing the list of social plugins. */ +std::unique_ptr MakeNWidgetSocialPlugins() +{ + return std::make_unique(); +} + struct GameOptionsWindow : Window { GameSettings *opt; bool reload; @@ -348,6 +526,16 @@ struct GameOptionsWindow : Window { } break; } + + case WID_GO_SOCIAL_PLUGIN_TITLE: + case WID_GO_SOCIAL_PLUGIN_PLATFORM: + case WID_GO_SOCIAL_PLUGIN_STATE: { + const NWidgetSocialPlugins *plugin = this->GetWidget(WID_GO_SOCIAL_PLUGINS); + assert(plugin != nullptr); + + plugin->SetStringParameters(widget); + break; + } } } @@ -390,7 +578,7 @@ struct GameOptionsWindow : Window { void SetTab(WidgetID widget) { - this->SetWidgetsLoweredState(false, WID_GO_TAB_GENERAL, WID_GO_TAB_GRAPHICS, WID_GO_TAB_SOUND); + this->SetWidgetsLoweredState(false, WID_GO_TAB_GENERAL, WID_GO_TAB_GRAPHICS, WID_GO_TAB_SOUND, WID_GO_TAB_SOCIAL); this->LowerWidget(widget); GameOptionsWindow::active_tab = widget; @@ -399,6 +587,7 @@ struct GameOptionsWindow : Window { case WID_GO_TAB_GENERAL: pane = 0; break; case WID_GO_TAB_GRAPHICS: pane = 1; break; case WID_GO_TAB_SOUND: pane = 2; break; + case WID_GO_TAB_SOCIAL: pane = 3; break; default: NOT_REACHED(); } @@ -493,6 +682,7 @@ struct GameOptionsWindow : Window { case WID_GO_TAB_GENERAL: case WID_GO_TAB_GRAPHICS: case WID_GO_TAB_SOUND: + case WID_GO_TAB_SOCIAL: this->SetTab(widget); break; @@ -814,6 +1004,7 @@ static constexpr NWidgetPart _nested_game_options_widgets[] = { NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_GENERAL), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_GENERAL, STR_GAME_OPTIONS_TAB_GENERAL_TT), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_GRAPHICS), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_GRAPHICS, STR_GAME_OPTIONS_TAB_GRAPHICS_TT), SetFill(1, 0), NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_SOUND), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_SOUND, STR_GAME_OPTIONS_TAB_SOUND_TT), SetFill(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_YELLOW, WID_GO_TAB_SOCIAL), SetMinimalTextLines(2, 0), SetDataTip(STR_GAME_OPTIONS_TAB_SOCIAL, STR_GAME_OPTIONS_TAB_SOCIAL_TT), SetFill(1, 0), EndContainer(), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY), @@ -969,6 +1160,11 @@ static constexpr NWidgetPart _nested_game_options_widgets[] = { EndContainer(), EndContainer(), EndContainer(), + + /* Social tab */ + NWidget(NWID_VERTICAL), SetPadding(WidgetDimensions::unscaled.sparse), SetPIP(0, WidgetDimensions::unscaled.vsep_wide, 0), + NWidgetFunction(MakeNWidgetSocialPlugins), + EndContainer(), EndContainer(), EndContainer(), }; diff --git a/src/widgets/settings_widget.h b/src/widgets/settings_widget.h index 4c40f1e0a0..fccba67a2b 100644 --- a/src/widgets/settings_widget.h +++ b/src/widgets/settings_widget.h @@ -15,6 +15,7 @@ enum GameOptionsWidgets : WidgetID { WID_GO_TAB_GENERAL, ///< General tab. WID_GO_TAB_GRAPHICS, ///< Graphics tab. WID_GO_TAB_SOUND, ///< Sound tab. + WID_GO_TAB_SOCIAL, ///< Social tab. WID_GO_TAB_SELECTION, ///< Background of the tab selection. WID_GO_CURRENCY_DROPDOWN, ///< Currency dropdown. WID_GO_DISTANCE_DROPDOWN, ///< Measuring unit dropdown. @@ -53,6 +54,10 @@ enum GameOptionsWidgets : WidgetID { WID_GO_SURVEY_PARTICIPATE_BUTTON, ///< Toggle for participating in the automated survey. WID_GO_SURVEY_LINK_BUTTON, ///< Button to open browser to go to the survey website. WID_GO_SURVEY_PREVIEW_BUTTON, ///< Button to open a preview window with the survey results + WID_GO_SOCIAL_PLUGINS, ///< Main widget handling the social plugins. + WID_GO_SOCIAL_PLUGIN_TITLE, ///< Title of the frame of the social plugin. + WID_GO_SOCIAL_PLUGIN_PLATFORM, ///< Platform of the social plugin. + WID_GO_SOCIAL_PLUGIN_STATE, ///< State of the social plugin. }; /** Widgets of the #GameSettingsWindow class. */