[App/Settings] Add support for range input and adjust number settings impl

This commit is contained in:
Satori 2021-10-02 15:34:04 +01:00
parent 5735eccacf
commit 566d992f76
4 changed files with 173 additions and 93 deletions

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="settings_schema.xsd"?>
<Sets> <Sets>
<Set name="General" icon=""> <Set name="General" icon="">
<Group name="General Settings"> <Group name="General Settings">
@ -79,6 +80,11 @@
<description>Enable Discord Rich Presence in Xenia</description> <description>Enable Discord Rich Presence in Xenia</description>
<cvar>discord</cvar> <cvar>discord</cvar>
</BooleanSetting> </BooleanSetting>
<BooleanSetting>
<title>Discord Rich Presence 2</title>
<description>Enable Discord Rich Presence in Xenia</description>
<cvar>discord</cvar>
</BooleanSetting>
<BooleanSetting> <BooleanSetting>
<title>Show game Icon in Taskbar</title> <title>Show game Icon in Taskbar</title>
<description>Show the currently loaded game's icon in the Taskbar</description> <description>Show the currently loaded game's icon in the Taskbar</description>
@ -136,12 +142,28 @@
<cvar>log_file</cvar> <cvar>log_file</cvar>
</PathInputSetting> </PathInputSetting>
</Group> </Group>
<Group name="Test Settings">
<RangeInputSetting>
<title>Test int32</title>
<description>A test int slider</description>
<cvar>test_int</cvar>
<type>int32</type>
<min>0</min>
<max>100</max>
</RangeInputSetting>
</Group>
</Set> </Set>
<Set name="CPU" icon=""> <Set name="CPU" icon="">
</Set> </Set>
<Set name="Graphics" icon=""> <Set name="Graphics" icon="">
<Group name="D3D12 Settings">
<MultiChoiceSetting>
<title>Resolution scale</title>
<description>Renders the game at native resolution or higher resolutions. Very GPU intensive. May cause issues in some games.</description>
</MultiChoiceSetting>
</Group>
</Set> </Set>
<Set name="Audio" icon=""> <Set name="Audio" icon="">

View File

@ -1,90 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Sets"> <xs:element name="Sets">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element maxOccurs="unbounded" ref="Set"/> <xs:element maxOccurs="unbounded" name="Set">
</xs:sequence> <xs:complexType mixed="true">
</xs:complexType> <xs:sequence minOccurs="0">
</xs:element> <xs:element maxOccurs="unbounded" name="Group">
<xs:element name="Set">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" ref="Group"/>
</xs:sequence>
<xs:attribute name="icon" use="required"/>
<xs:attribute name="name" use="required" type="xs:NCName"/>
</xs:complexType>
</xs:element>
<xs:element name="Group">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:choice maxOccurs="unbounded"> <xs:choice maxOccurs="unbounded">
<xs:element ref="BooleanSetting"/> <xs:element maxOccurs="unbounded" name="MultiChoiceSetting">
<xs:element ref="MultiChoiceSetting"/>
</xs:choice>
<xs:element minOccurs="0" ref="PathInputSetting"/>
</xs:sequence>
<xs:attribute name="name" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="BooleanSetting">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element ref="title"/> <xs:choice maxOccurs="unbounded">
<xs:element ref="description"/> <xs:element name="title" type="xs:string" />
<xs:element ref="cvar"/> <xs:element name="description" type="xs:string" />
</xs:sequence> <xs:element name="type" type="xs:string" />
</xs:complexType> <xs:element name="cvar" type="xs:string" />
</xs:element>
<xs:element name="MultiChoiceSetting">
<xs:complexType>
<xs:sequence>
<xs:element ref="title"/>
<xs:element ref="description"/>
<xs:element ref="type"/>
<xs:element minOccurs="0" ref="cvar"/>
<xs:element ref="options"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="type" type="xs:NCName"/>
<xs:element name="options"> <xs:element name="options">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element maxOccurs="unbounded" ref="option"/> <xs:element maxOccurs="unbounded" name="option">
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="option">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element ref="title"/> <xs:element name="title" type="xs:string" />
<xs:element ref="value"/> <xs:element name="value" type="xs:unsignedByte" />
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="value" type="xs:integer"/> </xs:sequence>
<xs:element name="TextInputSetting"> </xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element maxOccurs="unbounded" name="BooleanSetting">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element ref="title"/> <xs:element name="title" type="xs:string" />
<xs:element ref="description"/> <xs:element name="description" type="xs:string" />
<xs:element ref="cvar"/> <xs:element name="cvar" type="xs:string" />
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="PathInputSetting"> <xs:element maxOccurs="unbounded" name="PathInputSetting">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>
<xs:element ref="title"/> <xs:element name="title" type="xs:string" />
<xs:element ref="description"/> <xs:element name="description" type="xs:string" />
<xs:element ref="cvar"/> <xs:element name="cvar" type="xs:string" />
<xs:element name="require_valid_path" type="xs:boolean" minOccurs="0" /> </xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="icon" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="title" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="cvar" type="xs:NCName"/>
</xs:schema> </xs:schema>

View File

@ -115,9 +115,8 @@ inline int number_value_to_int(NumberValue v) {
template <SettingsType settings_type> template <SettingsType settings_type>
class NumberSettingsItem : public ISettingsItem { class NumberSettingsItem : public ISettingsItem {
public: struct ValueUpdater {
struct ValueVisitor { ValueUpdater(NumberSettingsItem* item) : item(item) {}
ValueVisitor(NumberSettingsItem* item) : item(item) {}
bool operator()(int8_t value) { return update(ValueType::Int8, value); } bool operator()(int8_t value) { return update(ValueType::Int8, value); }
bool operator()(int16_t value) { return update(ValueType::Int16, value); } bool operator()(int16_t value) { return update(ValueType::Int16, value); }
@ -144,6 +143,7 @@ class NumberSettingsItem : public ISettingsItem {
NumberSettingsItem* item; NumberSettingsItem* item;
}; };
public:
NumberSettingsItem(ValueType value_type, const std::string& title, NumberSettingsItem(ValueType value_type, const std::string& title,
const std::string& description, IConfigVar* cvar = nullptr) const std::string& description, IConfigVar* cvar = nullptr)
: ISettingsItem(settings_type, title, description), : ISettingsItem(settings_type, title, description),
@ -154,8 +154,44 @@ class NumberSettingsItem : public ISettingsItem {
IConfigVar* cvar() const { return cvar_; } IConfigVar* cvar() const { return cvar_; }
NumberValue current_value() const {
switch (value_type()) {
case ValueType::Int8: {
return *cvar()->as<int8_t>()->current_value();
}
case ValueType::Int16: {
return *cvar()->as<int16_t>()->current_value();
}
case ValueType::Int32: {
return *cvar()->as<int32_t>()->current_value();
}
case ValueType::Int64: {
return *cvar()->as<int64_t>()->current_value();
}
case ValueType::UInt8: {
return *cvar()->as<uint8_t>()->current_value();
}
case ValueType::UInt16: {
return *cvar()->as<uint16_t>()->current_value();
}
case ValueType::UInt32: {
return *cvar()->as<uint32_t>()->current_value();
}
case ValueType::UInt64: {
return *cvar()->as<uint64_t>()->current_value();
}
case ValueType::Double: {
return *cvar()->as<double>()->current_value();
}
default: {
assert_always("Invalid number type provided");
return 0;
}
}
}
virtual bool UpdateValue(NumberValue value) { virtual bool UpdateValue(NumberValue value) {
auto res = std::visit(ValueVisitor(this), value); auto res = std::visit(ValueUpdater(this), value);
if (!res) { if (!res) {
XELOGE("Could not update value for {0}. Incorrect value type provided", XELOGE("Could not update value for {0}. Incorrect value type provided",
cvar_->name()); cvar_->name());
@ -172,9 +208,8 @@ class NumberSettingsItem : public ISettingsItem {
class RangeInputSettingsItem : public NumberSettingsItem<SettingsType::Range> { class RangeInputSettingsItem : public NumberSettingsItem<SettingsType::Range> {
public: public:
RangeInputSettingsItem(ValueType value_type, std::string title, RangeInputSettingsItem(ValueType value_type, std::string title,
NumberValue min, NumberValue max, std::string description, NumberValue min,
std::string description = "", NumberValue max, IConfigVar* cvar = nullptr)
IConfigVar* cvar = nullptr)
: NumberSettingsItem(value_type, title, description, cvar), : NumberSettingsItem(value_type, title, description, cvar),
min_(min), min_(min),
max_(max) {} max_(max) {}
@ -205,6 +240,7 @@ class IMultiChoiceSettingsItem : public ISettingsItem {
virtual bool UpdateIndex(int index) = 0; virtual bool UpdateIndex(int index) = 0;
virtual std::vector<std::string> option_names() const = 0; virtual std::vector<std::string> option_names() const = 0;
virtual int current_index() const = 0; virtual int current_index() const = 0;
virtual IConfigVar* cvar() const = 0;
}; };
template <typename T> template <typename T>
@ -273,6 +309,8 @@ class MultiChoiceSettingsItem : public IMultiChoiceSettingsItem {
return names; return names;
} }
IConfigVar* cvar() const override { return cvar_; }
private: private:
ConfigVar<T>* cvar_; ConfigVar<T>* cvar_;
std::vector<Option> options_; std::vector<Option> options_;

View File

@ -14,6 +14,17 @@
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/config.h" #include "xenia/config.h"
#ifdef DEBUG
DEFINE_bool(test_bool, false, "test bool", "General");
DEFINE_int32(test_int, 0, "test bool", "General");
DEFINE_path(test_path, "C:\\", "test path", "General");
DEFINE_string(test_string, "C:\\", "test string", "General");
DEFINE_uint64(test_uint64, 0, "test uint64", "General");
DEFINE_double(test_double, 0, "test double", "General");
#endif // DEBUG
namespace xe { namespace xe {
namespace app { namespace app {
namespace settings { namespace settings {
@ -136,6 +147,35 @@ std::unique_ptr<ISettingsItem> SettingsLoader::LoadSettingsItemFromXmlNode(
return LoadMultiChoiceSetting<std::string>(title, description, cvar, return LoadMultiChoiceSetting<std::string>(title, description, cvar,
node); node);
} }
} else if (node_type == "RangeInputSetting") {
std::string_view value_type_str = node.child_value("type");
ValueType value_type;
if (value_type_str == "int8") {
value_type = ValueType::Int8;
} else if (value_type_str == "int16") {
value_type = ValueType::Int16;
} else if (value_type_str == "int32") {
value_type = ValueType::Int32;
} else if (value_type_str == "int64") {
value_type = ValueType::Int64;
} else if (value_type_str == "uint8") {
value_type = ValueType::UInt8;
} else if (value_type_str == "uint16") {
value_type = ValueType::UInt16;
} else if (value_type_str == "uint32") {
value_type = ValueType::UInt32;
} else if (value_type_str == "uint64") {
value_type = ValueType::UInt64;
} else if (value_type_str == "double") {
XELOGE("Floating point types are not supported for slider");
return nullptr;
}
int min = node.child("min").text().as_int();
int max = node.child("max").text().as_int();
return std::make_unique<RangeInputSettingsItem>(
value_type, title, description, min, max, cvar);
} }
} else { } else {
XELOGE("Unknown settings node type {}", node.name()); XELOGE("Unknown settings node type {}", node.name());
@ -251,7 +291,7 @@ void SettingsLoader::AddRangeInputSetting(std::string title,
NumberValue max) { NumberValue max) {
if (cvar) { if (cvar) {
auto setting_item = std::make_unique<RangeInputSettingsItem>( auto setting_item = std::make_unique<RangeInputSettingsItem>(
value_type, title, min, max, description, cvar); value_type, title, description, min, max, cvar);
AddSetting(std::move(setting_item), group, set); AddSetting(std::move(setting_item), group, set);
} else { } else {
XELOGE("Could not find cvar for setting with title {}", title); XELOGE("Could not find cvar for setting with title {}", title);