Source: gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControl.js

/**
 * The GameSettingControl is an abstract class that is inherited by game-setting control classes specific to a GUI-object type,
 * such as the GameSettingControlCheckbox or GameSettingControlDropdown.
 *
 * The purpose of these classes is to control one logical game setting.
 * The base classes allow implementing that while avoiding duplication.
 *
 * GameSettingControl classes watch for g_GameSettings property changes,
 * and re-render accordingly. They also trigger changes in g_GameSettings.
 *
 * The GameSettingControl classes are responsible for triggering network synchronisation,
 * and for updating the whole gamesetup layout when necessary.
 */
class GameSettingControl /* extends Profilable /* Uncomment to profile controls without hassle. */
{
	constructor(gameSettingControlManager, category, playerIndex, setupWindow)
	{
		// Store arguments
		{
			this.category = category;
			this.playerIndex = playerIndex;

			this.setupWindow = setupWindow;
			this.gameSettingsController = setupWindow.controls.gameSettingsController;
			this.mapCache = setupWindow.controls.mapCache;
			this.mapFilters = setupWindow.controls.mapFilters;
			this.netMessages = setupWindow.controls.netMessages;
			this.playerAssignmentsController = setupWindow.controls.playerAssignmentsController;
		}

		// enabled and hidden should only be modified through their setters or
		// by calling updateVisibility after modification.
		this.enabled = true;
		this.hidden = false;

		if (this.setControl)
			this.setControl(gameSettingControlManager);

		// This variable also used for autocompleting chat.
		this.autocompleteTitle = undefined;

		if (this.title && this.TitleCaption)
			this.setTitle(this.TitleCaption);

		if (this.Tooltip)
			this.setTooltip(this.Tooltip);

		this.setHidden(false);

		if (this.onLoad)
			this.setupWindow.registerLoadHandler(this.onLoad.bind(this));

		if (this.onSettingsLoaded)
			this.gameSettingsController.registerSettingsLoadedHandler(this.onSettingsLoaded.bind(this));

		if (this.onPlayerAssignmentsChange)
			this.playerAssignmentsController.registerPlayerAssignmentsChangeHandler(this.onPlayerAssignmentsChange.bind(this));
	}

	setTitle(titleCaption)
	{
		this.autocompleteTitle = titleCaption;
		this.title.caption = sprintf(this.TitleCaptionFormat, {
			"setting": titleCaption
		});
	}

	setTooltip(tooltip)
	{
		if (this.title)
			this.title.tooltip = tooltip;

		if (this.label)
			this.label.tooltip = tooltip;

		if (this.setControlTooltip)
			this.setControlTooltip(tooltip);
	}

	setEnabled(enabled)
	{
		this.enabled = enabled;
		this.updateVisibility();
	}

	setHidden(hidden)
	{
		this.hidden = hidden;
		// Trigger a layout update to reposition items.
		this.gameSettingsController.updateLayout();
	}

	updateVisibility()
	{
		let hidden =
			this.hidden ||
			this.playerIndex === undefined &&
				this.category != g_TabCategorySelected ||
					this.playerIndex !== undefined &&
					this.playerIndex >= g_GameSettings.playerCount.nbPlayers;

		if (this.frame)
			this.frame.hidden = hidden;

		if (hidden)
			return;

		let enabled = g_IsController && this.enabled;

		if (this.setControlHidden)
			this.setControlHidden(!enabled);

		if (this.label)
			this.label.hidden = !!enabled;
	}

	/**
	 * Returns whether the control specifies an order but didn't implement the function.
	 */
	addAutocompleteEntries(name, autocomplete)
	{
		if (this.autocompleteTitle)
			autocomplete[0].push(this.autocompleteTitle);

		if (!Number.isInteger(this.AutocompleteOrder))
			return;

		if (!this.getAutocompleteEntries)
		{
			error(name + " specifies AutocompleteOrder but didn't implement getAutocompleteEntries");
			return;
		}

		let newEntries = this.getAutocompleteEntries();
		if (newEntries)
			autocomplete[this.AutocompleteOrder] =
				(autocomplete[this.AutocompleteOrder] || []).concat(newEntries);
	}
}

GameSettingControl.prototype.TitleCaptionFormat =
	translateWithContext("Title for specific setting", "%(setting)s:");

/**
 * Derived classes can set this to a number to enable chat autocompleting of setting values.
 * Higher numbers are autocompleted first.
 */
GameSettingControl.prototype.AutocompleteOrder = undefined;