forked from 0ad/0ad
Rework the logics of options loading to allow more flexibility (i.e. adding a min or max value for number inputs)
Fix some problems when reloading saved options, refs #3511 Add the material quality option, refs #3737 Reload and Save buttons are only enabled when some options have changed. This was SVN commit r17645.
This commit is contained in:
parent
cd22f9f722
commit
5c61f68600
@ -1,5 +1,5 @@
|
||||
var g_hasCallback = false;
|
||||
var g_hasChanges = false;
|
||||
var g_controls;
|
||||
/**
|
||||
* This array holds the data to populate the general section with.
|
||||
* Data is in the form [Title, Tooltip, {ActionType:Action}, InputType].
|
||||
@ -35,18 +35,19 @@ var options = {
|
||||
[translate("Shadows on Water"), translate("Cast shadows on water"), {"renderer":"WaterShadows", "config":"watershadows"}, "boolean"],
|
||||
[translate("VSync"), translate("Run vertical sync to fix screen tearing. REQUIRES GAME RESTART"), {"config":"vsync"}, "boolean"],
|
||||
[translate("Limit FPS in Menus"), translate("Limit FPS to 50 in all menus, to save power."), {"config":"gui.menu.limitfps"}, "boolean"],
|
||||
[translate("Graphics quality"), translate("Graphics quality. REQUIRES GAME RESTART"), {"config":"materialmgr.quality", "min": "0", "max": "10"}, "number"],
|
||||
],
|
||||
"soundSetting":
|
||||
[
|
||||
[translate("Master Gain"), translate("Master audio gain"), {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));"}, "number"],
|
||||
[translate("Music Gain"), translate("In game music gain"), {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
|
||||
[translate("Ambient Gain"), translate("In game ambient sound gain"), {"config":"sound.ambientgain", "function":"Engine.SetAmbientGain(Number(this.caption));"}, "number"],
|
||||
[translate("Action Gain"), translate("In game unit action sound gain"), {"config":"sound.actiongain", "function":"Engine.SetActionGain(Number(this.caption));"}, "number"],
|
||||
[translate("UI Gain"), translate("UI sound gain"), {"config":"sound.uigain", "function":"Engine.SetUIGain(Number(this.caption));"}, "number"],
|
||||
[translate("Master Gain"), translate("Master audio gain"), {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));", "min": "0"}, "number"],
|
||||
[translate("Music Gain"), translate("In game music gain"), {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));", "min": "0"}, "number"],
|
||||
[translate("Ambient Gain"), translate("In game ambient sound gain"), {"config":"sound.ambientgain", "function":"Engine.SetAmbientGain(Number(this.caption));", "min": "0"}, "number"],
|
||||
[translate("Action Gain"), translate("In game unit action sound gain"), {"config":"sound.actiongain", "function":"Engine.SetActionGain(Number(this.caption));", "min": "0"}, "number"],
|
||||
[translate("UI Gain"), translate("UI sound gain"), {"config":"sound.uigain", "function":"Engine.SetUIGain(Number(this.caption));", "min": "0"}, "number"],
|
||||
],
|
||||
"lobbySetting":
|
||||
[
|
||||
[translate("Chat Backlog"), translate("Number of backlogged messages to load when joining the lobby"), {"config":"lobby.history"}, "number"],
|
||||
[translate("Chat Backlog"), translate("Number of backlogged messages to load when joining the lobby"), {"config":"lobby.history", "min": "0"}, "number"],
|
||||
[translate("Chat Timestamp"), translate("Show time that messages are posted in the lobby chat"), {"config":"lobby.chattimestamp"}, "boolean"],
|
||||
],
|
||||
};
|
||||
@ -56,6 +57,7 @@ function init(data)
|
||||
if (data && data.callback)
|
||||
g_hasCallback = true;
|
||||
let reload = data && data.reload;
|
||||
g_controls = [];
|
||||
|
||||
// WARNING: We assume a strict formatting of the XML and do minimal checking.
|
||||
for (let prefix of Object.keys(options))
|
||||
@ -87,6 +89,9 @@ function init(data)
|
||||
body.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reload)
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,7 +103,7 @@ function init(data)
|
||||
function setupControl(option, i, prefix, reload)
|
||||
{
|
||||
var control;
|
||||
var onPress = function(){ g_hasChanges = true; };
|
||||
var onPress;
|
||||
|
||||
switch (option[3])
|
||||
{
|
||||
@ -110,56 +115,51 @@ function setupControl(option, i, prefix, reload)
|
||||
text.size = size;
|
||||
control = Engine.GetGUIObjectByName(prefix + "Tickbox[" + i + "]");
|
||||
var checked;
|
||||
// Different option action load and save differently, so this switch is needed.
|
||||
var keyRenderer;
|
||||
var keyConfig;
|
||||
var functionBody;
|
||||
|
||||
for (let action of Object.keys(option[2]))
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case "config":
|
||||
// Load initial value if not yet loaded.
|
||||
if (!checked || typeof checked != "boolean")
|
||||
checked = Engine.ConfigDB_GetValue("user", option[2][action]) === "true";
|
||||
// Hacky macro to create the callback.
|
||||
var callback = function(key)
|
||||
{
|
||||
return function()
|
||||
Engine.ConfigDB_CreateValue("user", key, String(this.checked));
|
||||
}(option[2][action]);
|
||||
// Merge the new callback with any existing callbacks.
|
||||
onPress = mergeFunctions(callback, onPress);
|
||||
keyConfig = option[2].config;
|
||||
if (checked === undefined || reload)
|
||||
checked = Engine.ConfigDB_GetValue("user", keyConfig) === "true";
|
||||
else if ((Engine.ConfigDB_GetValue("user", keyConfig) === "true") !== checked)
|
||||
Engine.ConfigDB_CreateValue("user", keyConfig, String(checked));
|
||||
break;
|
||||
case "renderer":
|
||||
// If reloading, config values have priority, otherwise load initial value if not yet loaded
|
||||
if (reload && option[2].config)
|
||||
{
|
||||
checked = Engine.ConfigDB_GetValue("user", option[2].config) === "true";
|
||||
let rendererChecked = eval("Engine.Renderer_Get" + option[2].renderer + "Enabled()");
|
||||
if (rendererChecked != checked)
|
||||
eval("Engine.Renderer_Set" + option[2].renderer + "Enabled(" + checked + ")");
|
||||
}
|
||||
else if (!checked || typeof checked != "boolean")
|
||||
checked = eval("Engine.Renderer_Get" + option[2][action] + "Enabled()");
|
||||
// Hacky macro to create the callback (updating also the config value if any).
|
||||
var callback = function(key, keyConfig)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
eval("Engine.Renderer_Set" + key + "Enabled(" + this.checked + ")");
|
||||
if (keyConfig)
|
||||
Engine.ConfigDB_CreateValue("user", keyConfig, String(this.checked));
|
||||
};
|
||||
}(option[2][action], option[2].config);
|
||||
// Merge the new callback with any existing callbacks.
|
||||
onPress = mergeFunctions(callback, onPress);
|
||||
keyRenderer = option[2].renderer;
|
||||
if (checked === undefined)
|
||||
checked = eval("Engine.Renderer_Get" + keyRenderer + "Enabled()");
|
||||
else if (eval("Engine.Renderer_Get" + keyRenderer + "Enabled()") !== checked)
|
||||
eval("Engine.Renderer_Set" + keyRenderer + "Enabled(" + checked + ")");
|
||||
break;
|
||||
case "function":
|
||||
// This allows for doing low-level actions, like hiding/showing UI elements.
|
||||
onPress = mergeFunctions(eval("function(){" + option[2][action] + "}"), onPress);
|
||||
functionBody = option[2].function;
|
||||
break;
|
||||
default:
|
||||
warn("Unknown option source type '" + action + "'");
|
||||
}
|
||||
}
|
||||
|
||||
onPress = function(keyRenderer, keyConfig, functionBody)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
if (keyRenderer)
|
||||
eval("Engine.Renderer_Set" + keyRenderer + "Enabled(" + this.checked + ")");
|
||||
if (keyConfig)
|
||||
Engine.ConfigDB_CreateValue("user", keyConfig, String(this.checked));
|
||||
if (functionBody)
|
||||
eval(functionBody);
|
||||
updateStatus(true);
|
||||
};
|
||||
}(keyRenderer, keyConfig, functionBody);
|
||||
|
||||
// Load final data to the control element.
|
||||
control.checked = checked;
|
||||
control.onPress = onPress;
|
||||
@ -169,41 +169,62 @@ function setupControl(option, i, prefix, reload)
|
||||
case "string":
|
||||
control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
|
||||
var caption;
|
||||
var key;
|
||||
var functionBody;
|
||||
var minval;
|
||||
var maxval;
|
||||
|
||||
for (let action of Object.keys(option[2]))
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case "config":
|
||||
onPress = function(){};
|
||||
caption = Engine.ConfigDB_GetValue("user", option[2][action]);
|
||||
// Hacky macro to create the callback.
|
||||
var callback = function(key)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
if (Engine.ConfigDB_GetValue("user", key) == this.caption)
|
||||
return;
|
||||
Engine.ConfigDB_CreateValue("user", key, String(this.caption));
|
||||
g_hasChanges = true;
|
||||
};
|
||||
}(option[2][action]);
|
||||
// Merge the new callback with any existing callbacks.
|
||||
onPress = mergeFunctions(callback, onPress);
|
||||
key = option[2].config;
|
||||
caption = Engine.ConfigDB_GetValue("user", key);
|
||||
break;
|
||||
case "function":
|
||||
// This allows for doing low-level actions, like hiding/showing UI elements.
|
||||
onPress = mergeFunctions(function(){eval(option[2][action]);}, onPress);
|
||||
functionBody = option[2].function;
|
||||
break;
|
||||
case "min":
|
||||
minval = option[2].min;
|
||||
break;
|
||||
case "max":
|
||||
maxval = option[2].max;
|
||||
break;
|
||||
default:
|
||||
warn("Unknown option source type '" + action + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// as the enter key is not necessarily pressed after modifying an entry, we will register the input also
|
||||
// - when the mouse leave the control (MouseLeave event)
|
||||
// - or when saving or closing the window (registerChanges function)
|
||||
// so we must ensure that something has indeed been modified
|
||||
onPress = function(key, functionBody, minval, maxval)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
if (minval && +minval > +this.caption)
|
||||
this.caption = minval;
|
||||
if (maxval && +maxval < +this.caption)
|
||||
this.caption = maxval;
|
||||
if (Engine.ConfigDB_GetValue("user", key) === this.caption)
|
||||
return;
|
||||
Engine.ConfigDB_CreateValue("user", key, this.caption);
|
||||
if (functionBody)
|
||||
eval(functionBody);
|
||||
updateStatus(true);
|
||||
};
|
||||
}(key, functionBody, minval, maxval);
|
||||
|
||||
control.caption = caption;
|
||||
control.onPress = onPress;
|
||||
control.onMouseLeave = onPress;
|
||||
g_controls.push(control);
|
||||
break;
|
||||
default:
|
||||
warn("Unknown option type '" + options[3] + "', assuming string. Valid types are 'number', 'string', or 'bool'.");
|
||||
warn("Unknown option type '" + option[3] + "', assuming string. Valid types are 'number', 'string', or 'bool'.");
|
||||
control = Engine.GetGUIObjectByName(prefix + "Input[" + i + "]");
|
||||
break;
|
||||
}
|
||||
@ -212,31 +233,38 @@ function setupControl(option, i, prefix, reload)
|
||||
return control;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two functions which don't expect arguments.
|
||||
*
|
||||
* @return Merged function.
|
||||
*/
|
||||
function mergeFunctions(function1, function2)
|
||||
function updateStatus(val)
|
||||
{
|
||||
return function()
|
||||
{
|
||||
function1.apply(this);
|
||||
function2.apply(this);
|
||||
};
|
||||
if (typeof val == "boolean")
|
||||
Engine.ConfigDB_CreateValue("user", "nosave.haschanges", String(val));
|
||||
else
|
||||
val = Engine.ConfigDB_GetValue("user", "nosave.haschanges") === "true";
|
||||
|
||||
Engine.GetGUIObjectByName("loadOptions").enabled = val;
|
||||
Engine.GetGUIObjectByName("saveOptions").enabled = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register changes of input (text and number) controls
|
||||
*/
|
||||
function registerChanges()
|
||||
{
|
||||
for (let control of g_controls)
|
||||
control.onPress();
|
||||
}
|
||||
|
||||
function reloadDefaults()
|
||||
{
|
||||
Engine.ConfigDB_Reload("user");
|
||||
updateStatus(false);
|
||||
init({ "reload": true });
|
||||
g_hasChanges = false;
|
||||
}
|
||||
|
||||
function saveDefaults()
|
||||
{
|
||||
g_hasChanges = false;
|
||||
registerChanges();
|
||||
Engine.ConfigDB_WriteFile("user", "config/user.cfg");
|
||||
updateStatus(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,7 +272,8 @@ function saveDefaults()
|
||||
**/
|
||||
function closePage()
|
||||
{
|
||||
if (g_hasChanges)
|
||||
registerChanges();
|
||||
if (Engine.ConfigDB_GetValue("user", "nosave.haschanges") === "true")
|
||||
{
|
||||
let btCaptions = [translate("No"), translate("Yes")];
|
||||
let btCode = [null, function(){ closePageWithoutConfirmation(); }];
|
||||
|
@ -65,14 +65,14 @@
|
||||
</object>
|
||||
</repeat>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%-170 100%-44 50%-70 100%-16" hotkey="cancel">
|
||||
<object name="loadOptions" type="button" style="ModernButtonRed" size="50%-170 100%-44 50%-70 100%-16" hotkey="cancel">
|
||||
<translatableAttribute id="caption">Reload</translatableAttribute>
|
||||
<translatableAttribute id="tooltip">Reload default values from file</translatableAttribute>
|
||||
<translatableAttribute id="tooltip">Reload previous saved settings</translatableAttribute>
|
||||
<action on="Press">reloadDefaults();</action>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%-62 100%-44 50%+38 100%-16">
|
||||
<object name="saveOptions" type="button" style="ModernButtonRed" size="50%-62 100%-44 50%+38 100%-16">
|
||||
<translatableAttribute id="caption">Save</translatableAttribute>
|
||||
<translatableAttribute id="tooltip">Save changes to file</translatableAttribute>
|
||||
<translatableAttribute id="tooltip">Save changes</translatableAttribute>
|
||||
<action on="Press">saveDefaults();</action>
|
||||
</object>
|
||||
<object type="button" style="ModernButtonRed" size="50%+70 100%-44 50%+170 100%-16">
|
||||
|
@ -378,6 +378,8 @@ bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path) const
|
||||
char* pos = (char*)buf.get();
|
||||
for (const std::pair<CStr, CConfigValueSet>& p : m_Map[ns])
|
||||
{
|
||||
if (boost::algorithm::starts_with(p.first, "nosave."))
|
||||
continue;
|
||||
size_t i;
|
||||
pos += sprintf(pos, "%s = ", p.first.c_str());
|
||||
for (i = 0; i < p.second.size() - 1; ++i)
|
||||
|
Loading…
Reference in New Issue
Block a user