Add settings API and specification

The details of the API are in the `/docs/specsSettings-API.md` file.

The enums do not list all available settings, more are to be added as
the settings API replaces inline QSettings calls.
This commit is contained in:
Vasily
2019-12-03 08:26:10 +01:00
committed by username223
parent 61fd3dde79
commit c1690056cc
13 changed files with 1131 additions and 5 deletions
+10 -4
View File
@@ -15,10 +15,11 @@ SOURCES += src/main.cpp\
src/openvr/ivrinput.cpp \
src/utils/setup.cpp \
src/utils/paths.cpp \
src/utils/FrameRateUtils.cpp \
src/utils/FrameRateUtils.cpp \
src/openvr/overlay_utils.cpp \
src/keyboard_input/keyboard_input.cpp \
src/keyboard_input/input_parser.cpp
src/keyboard_input/input_parser.cpp \
src/settings/settings.cpp
HEADERS += src/overlaycontroller.h \
src/tabcontrollers/AudioTabController.h \
@@ -46,10 +47,15 @@ HEADERS += src/overlaycontroller.h \
src/openvr/ivrinput.h \
src/utils/setup.h \
src/utils/paths.h \
src/utils/FrameRateUtils.h \
src/utils/FrameRateUtils.h \
src/openvr/overlay_utils.h \
src/keyboard_input/input_parser.h \
src/keyboard_input/input_sender.h
src/keyboard_input/input_sender.h \
src/settings/settings.h \
src/settings/setting_value.h \
src/settings/settings_internal.h \
src/settings/settings_controller.h \
src/settings/specific_setting_value.h
win32 {
SOURCES += src/tabcontrollers/audiomanager/AudioManagerWindows.cpp \
+228
View File
@@ -0,0 +1,228 @@
## Problem
The current solution for storing and getting settings is to directly use the Qt QSettings object.
This leads to extremely verbose code for something that really should just be a single function call.
While the Qt settings management abstracts low level details very well, an even higher level API should be sought after.
The current system has stringly typed settings, which means it's possible to save the `desktopWidth` setting, but attempt to use the `deskWidth` setting somewhere else without noticing.
Currently, there are many different ways for a variable to be initialized depending on what goes wrong (QML, header file initialization, constructor, QSettings loading).
There should only default option when none other are available.
## Considerations for the new Settings API
The public facing API should be decoupled from the Qt API, in case we want to use another API later.
There should be a single correct way of referring to a setting.
The public API should not concern itself with how the settings are stored (.ini file/database/PROM).
The public API should be simple, and return error checked, ready values.
The settings should be logically grouped.
Ideally, the type system is used to ensure that a setting isn't used as the wrong type (at compile time?).
Ideally, it should not be possible to attempt to retrieve non-existent settings.
## Requirements
The API MUST transparently allow the storage of persistent values. The performance impact during runtime SHOULD be as small as possible.
Settings set using the API MUST be recoverable in the event of an unexpected program termination.
The API MUST allow for easy addition of new settings, with minimal vectors for error.
The API MUST type check the values, so that it is not possible to misinterpret data.
The API MUST have a default value for all settings that is used in case previously stored values are unavailable.
## Public API
The public API is in the `settings.h` file in the `settings` namespace.
The settings are represented as enums according to type.
Each option is prefixed with their general category (`PLAYSPACE_`, etc.).
```cpp
enum class BoolSetting
{
PLAYSPACE_lockXToggle,
PLAYSPACE_lockYToggle,
PLAYSPACE_lockZToggle,
[...]
CHAPERONE_disableChaperone,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = CHAPERONE_disableChaperone,
};
enum class DoubleSetting
{
UTILITY_desktopWidth,
[...]
CHAPERONE_fadeDistanceRemembered,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = CHAPERONE_fadeDistanceRemembered,
};
enum class StringSetting
{
KEYBOARDSHORTCUT_keyboardOne,
KEYBOARDSHORTCUT_keyboardTwo,
KEYBOARDSHORTCUT_keyboardThree,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = KEYBOARDSHORTCUT_keyboardThree,
};
enum class IntSetting
{
UTILITY_alarmHour,
UTILITY_alarmMinute,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = UTILITY_alarmMinute,
};
```
The order of the of the enumerators matter, and they can't be reordered without leading to a runtime error.
The `LAST_ENUMERATOR` is a result of C++ enums not knowing how many enumerators they have, and it is required for the implementation to work correctly.
Values can be accessed with imperative functions.
```cpp
[[nodiscard]] bool getSetting( const BoolSetting setting );
void setSetting( const BoolSetting setting, const bool value );
[[nodiscard]] double getSetting( const DoubleSetting setting );
void setSetting( const DoubleSetting setting, const double value );
[[nodiscard]] int getSetting( const IntSetting setting );
void setSetting( const IntSetting setting, const int value );
[[nodiscard]] std::string getSetting( const StringSetting setting );
void setSetting( const StringSetting setting, const std::string value );
```
The `[[nodiscard]]` means that it's a compiler error to not use the return value of the function.
Since none of the getters have side effects it would always be wrong to call the function without using the value.
Additionally, there are three utility functions.
```cpp
std::string initializeAndGetSettingsPath();
void saveChangedSettings();
void saveAllSettings();
```
`saveChangedSettings()` only writes settings to disk that have had their values changed through the API.
`saveAllSettings()` writes all known settings to disk.
`initializeAndGetSettingsPath()` initializes the API and returns the full path of the settings file.
It is not a requirement for this function to be called for the API to work.
If it is not called, the API will be initialized with the first call to the API.
The function is used as a means of controlling when the API will be initialized, as well as getting the current file path.
## Implementation
The public imperative API is an abstraction for the implementation, which is contained in a `SettingsController`.
The `SettingsController` object is a `static` object inside the `getSettingsController()` function.
This is done to remove the limitations that an object oriented design would bring, namely lifetime and scope management.
Since the API is supposed to be globally available throughout the lifetime of the program, creating it as an object in the `OverlayController` and passing it to subcontrollers would only introduce unnecessary parameter passing.
The `SettingsController` also relies on containing the settings as mutable state, and doing so in a purely imperative way would be cumbersome.
The API currently supports `int`, `double`, `std::string` and `bool` values. Below the `int` version is examined but the others are identical in implementation.
Settings are stored as an array of `*SettingValue`s the size of the `*Setting` enum.
```cpp
constexpr static auto intSettingsSize
= static_cast<int>( IntSetting::LAST_ENUMERATOR ) + 1;
std::array<IntSettingValue, intSettingsSize> m_intSettings{
IntSettingValue{ IntSetting::UTILITY_alarmHour,
SettingCategory::Utility,
QtInfo{ "alarmHour" },
0 },
IntSettingValue{ IntSetting::UTILITY_alarmMinute,
SettingCategory::Utility,
QtInfo{ "alarmMinute" },
0 },
};
```
Here, for each setting, the following is defined:
* `setting`: Which enumerator the setting has.
* `category`: Which category the setting belongs to (used for QSettings categories).
* `QtInfo`: A struct that contains Qt specific information. For now only a string value containing the name of the setting.
* The default value of the setting.
The `*SettingValue` classes are template specializations of the `SpecificSettingValue` class.
```cpp
using IntSettingValue = SpecificSettingValue<int, IntSetting>;
```
The `SpecificSettingValue` class contains getters for the `setting`, `category` and `qtInfo` fields, as well as a getter+setter for the `value` field.
```cpp
template <typename Value, typename Setting>
class SpecificSettingValue : public SettingValue
{
public:
SpecificSettingValue( const Setting setting,
const SettingCategory category,
const QtInfo qtInfo,
const Value defaultValue )
: SettingValue( category, qtInfo ), m_setting( setting ),
m_value( defaultValue )
{
// Attempt to load saved setting
// Use default if none saved
// Save setting if not saved to make it appear in .ini file
}
```
The templates used are not very complex. For the `IntSettingValue` you would simply mentally replace `Setting` with `IntSetting` and `Value` with `int`.
The templates reduce code duplication by a very large amount, which is why they are used.
The `SpecificSettingValue` inherits from the `SettingValue` base class. This class only contains fields for `category` and `qtInfo`.
The inheritance from `SettingValue` is necessary in order to make the `saveChangedValues()` easier to implement.
When a setting has changed value it is added to an array of `SettingValue`s (the base class).
It is possible to call `saveValue()` on all derived classes since it has been defined as a `virtual` function in the base class.
This is done in the `saveChangedValues()` function.
Templates might seem daunting if when they are encountered for the first time, but the usage here is not very advanced.
```cpp
template <typename ReturnType, typename Setting>
[[nodiscard]] ReturnType getSetting( const Setting setting ) const noexcept
{
const auto index = static_cast<std::size_t>( setting );
if constexpr ( std::is_same<Setting, BoolSetting>::value )
{
return m_boolSettings[index].value();
}
else if constexpr ( std::is_same<Setting, DoubleSetting>::value )
{
return m_doubleSettings[index].value();
}
else if constexpr ( std::is_same<Setting, IntSetting>::value )
{
return m_intSettings[index].value();
}
else if constexpr ( std::is_same<Setting, StringSetting>::value )
{
return m_stringSettings[index].value();
}
}
```
The above is the generalized `getSetting()` function that takes a generalized `Setting` as a parameter, and returns a generalized `ReturnType`.
Following the `IntSettingValue` example we would pass an `IntSetting` enum to `Setting` and `int` to `ReturnType`.
All our enums can be cast as integers, so we do that outside of the big `if` block.
Next we need to specify which array we index in to get the relevant setting, so we use `if constexpr` and `std::is_same<A, B>::value` to find which type we are dealing with.
This will compile different functions for the different types, so the `if constexpr` statement would not get executed.
The alternative to this would be having `n` amount of specific functions with predefined types.
This is done in the public API for explicitness, but internally it is much easier to maintain a single function.
The above function compiles down to the equivalent of four manually typed functions.
+2 -1
View File
@@ -1,4 +1,5 @@
#include "utils/setup.h"
#include "settings/settings.h"
INITIALIZE_EASYLOGGINGPP
@@ -40,7 +41,7 @@ int main( int argc, char* argv[] )
mainEventLoop.applicationName() );
advsettings::OverlayController::setAppSettings( &appSettings );
LOG( INFO ) << "Settings File: "
<< appSettings.fileName().toStdString();
<< settings::initializeAndGetSettingsPath();
QQmlEngine qmlEngine;
+5
View File
@@ -22,6 +22,7 @@
#include "utils/Matrix.h"
#include "openvr/overlay_utils.h"
#include "keyboard_input/input_sender.h"
#include "settings/settings.h"
// application namespace
namespace advsettings
@@ -1114,6 +1115,9 @@ void OverlayController::mainEventLoop()
LOG( INFO ) << "Received quit request.";
vr::VRSystem()->AcknowledgeQuit_Exiting(); // Let us buy some
// time just in case
settings::saveAllSettings();
m_moveCenterTabController.shutdown();
// Un-mute mic before Exiting VR, as it is set at system level Not
// Vr level.
@@ -1140,6 +1144,7 @@ void OverlayController::mainEventLoop()
{
LOG( DEBUG ) << "Dashboard deactivated";
m_dashboardVisible = false;
settings::saveChangedSettings();
}
break;
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <QSettings>
#include "settings.h"
#include "settings_internal.h"
#include "setting_value.h"
namespace settings
{
class BoolSettingValue : public SettingValue
{
public:
BoolSettingValue( const BoolSetting setting,
const SettingCategory category,
const QtInfo qtInfo,
const bool defaultValue )
: SettingValue( category, qtInfo ), m_setting( setting ),
m_value( defaultValue )
{
const auto v = getQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName );
if ( isValidQVariant( v, QMetaType::Bool ) )
{
m_value = v.toBool();
}
}
void setValue( const bool value ) noexcept
{
m_value = value;
}
bool value() const noexcept
{
return m_value;
}
BoolSetting setting() const noexcept
{
return m_setting;
}
SettingCategory category() const noexcept
{
return SettingValue::category();
}
QtInfo qtInfo() const noexcept
{
return SettingValue::qtInfo();
}
void saveValue() override
{
saveQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName,
m_value );
}
private:
const BoolSetting m_setting;
bool m_value;
};
} // namespace settings
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <QSettings>
#include "settings.h"
#include "settings_internal.h"
#include "setting_value.h"
namespace settings
{
class DoubleSettingValue : public SettingValue
{
public:
DoubleSettingValue( const DoubleSetting setting,
const SettingCategory category,
const QtInfo qtInfo,
const double defaultValue )
: SettingValue( category, qtInfo ), m_setting( setting ),
m_value( defaultValue )
{
const auto v = getQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName );
if ( isValidQVariant( v, QMetaType::Double ) )
{
m_value = v.toDouble();
}
}
void setValue( const double value ) noexcept
{
m_value = value;
}
double value() const noexcept
{
return m_value;
}
DoubleSetting setting() const noexcept
{
return m_setting;
}
SettingCategory category() const noexcept
{
return SettingValue::category();
}
QtInfo qtInfo() const noexcept
{
return SettingValue::qtInfo();
}
void saveValue() override
{
saveQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName,
m_value );
}
private:
const DoubleSetting m_setting;
double m_value;
};
} // namespace settings
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include "settings_internal.h"
namespace settings
{
class SettingValue
{
public:
SettingValue( const SettingCategory category, const QtInfo qtInfo ) noexcept
: m_category{ category }, m_qtInfo{ qtInfo }
{
}
virtual ~SettingValue() {}
[[nodiscard]] SettingCategory category() const noexcept
{
return m_category;
}
[[nodiscard]] QtInfo qtInfo() const noexcept
{
return m_qtInfo;
}
virtual void saveValue() = 0;
private:
const SettingCategory m_category;
const QtInfo m_qtInfo;
};
} // namespace settings
+76
View File
@@ -0,0 +1,76 @@
#include <utility>
#include <QSettings>
#include <easylogging++.h>
#include "../overlaycontroller.h"
#include "settings_controller.h"
namespace settings
{
SettingsController& getSettingsController()
{
static SettingsController s{};
return s;
}
void saveChangedSettings()
{
getSettingsController().saveChangedSettings();
}
void saveAllSettings()
{
LOG( INFO ) << "Saving all settings.";
getSettingsController().saveAllSettings();
LOG( INFO ) << "All settings saved.";
}
[[nodiscard]] bool getSetting( const BoolSetting setting )
{
return getSettingsController().getSetting<bool>( setting );
}
void setSetting( const BoolSetting setting, const bool value )
{
getSettingsController().setSetting( setting, value );
}
[[nodiscard]] double getSetting( const DoubleSetting setting )
{
return getSettingsController().getSetting<double>( setting );
}
void setSetting( const DoubleSetting setting, const double value )
{
getSettingsController().setSetting( setting, value );
}
[[nodiscard]] int getSetting( const IntSetting setting )
{
return getSettingsController().getSetting<int>( setting );
}
void setSetting( const IntSetting setting, const int value )
{
getSettingsController().setSetting( setting, value );
}
[[nodiscard]] std::string getSetting( const StringSetting setting )
{
return getSettingsController().getSetting<std::string>( setting );
}
void setSetting( const StringSetting setting, const std::string value )
{
getSettingsController().setSetting( setting, value );
}
std::string initializeAndGetSettingsPath()
{
// The static object is initialized the first time the function is called.
// Having a dedicated function for this allows more control over when this
// happens.
return getSettingsController().getSettingsFileName();
}
} // namespace settings
+93
View File
@@ -0,0 +1,93 @@
#pragma once
#include <string>
namespace settings
{
enum class BoolSetting
{
PLAYSPACE_lockXToggle,
PLAYSPACE_lockYToggle,
PLAYSPACE_lockZToggle,
PLAYSPACE_momentumSave,
PLAYSPACE_turnBindLeft,
PLAYSPACE_turnBindRight,
PLAYSPACE_turnBounds,
PLAYSPACE_moveShortcutLeft,
PLAYSPACE_moveShortcutRight,
PLAYSPACE_dragBounds,
PLAYSPACE_allowExternalEdits,
PLAYSPACE_oldStyleMotion,
PLAYSPACE_universeCenteredRotation,
PLAYSPACE_enableSeatedMotion,
APPLICATION_disableVersionCheck,
APPLICATION_previousShutdownSafe,
VIDEO_brightnessEnabled,
VIDEO_isOverlayMethodActive,
VIDEO_colorOverlayEnabled,
CHAPERONE_disableChaperone,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = CHAPERONE_disableChaperone,
};
enum class DoubleSetting
{
UTILITY_desktopWidth,
VIDEO_brightnessOpacityValue,
VIDEO_brightnessValue,
VIDEO_colorOverlayOpacity,
VIDEO_colorRed,
VIDEO_colorGreen,
VIDEO_colorBlue,
VIDEO_colorRedNew,
VIDEO_colorGreenNew,
VIDEO_colorBlueNew,
CHAPERONE_switchToBeginnerDistance,
CHAPERONE_hapticFeedbackDistance,
CHAPERONE_alarmSoundDistance,
CHAPERONE_showDashboardDistance,
CHAPERONE_fadeDistanceRemembered,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = CHAPERONE_fadeDistanceRemembered,
};
enum class StringSetting
{
KEYBOARDSHORTCUT_keyboardOne,
KEYBOARDSHORTCUT_keyboardTwo,
KEYBOARDSHORTCUT_keyboardThree,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = KEYBOARDSHORTCUT_keyboardThree,
};
enum class IntSetting
{
UTILITY_alarmHour,
UTILITY_alarmMinute,
// LAST_ENUMERATOR must always be set to the last value
LAST_ENUMERATOR = UTILITY_alarmMinute,
};
std::string initializeAndGetSettingsPath();
void saveChangedSettings();
void saveAllSettings();
[[nodiscard]] bool getSetting( const BoolSetting setting );
void setSetting( const BoolSetting setting, const bool value );
[[nodiscard]] double getSetting( const DoubleSetting setting );
void setSetting( const DoubleSetting setting, const double value );
[[nodiscard]] int getSetting( const IntSetting setting );
void setSetting( const IntSetting setting, const int value );
[[nodiscard]] std::string getSetting( const StringSetting setting );
void setSetting( const StringSetting setting, const std::string value );
} // namespace settings
+337
View File
@@ -0,0 +1,337 @@
#pragma once
#include <assert.h>
#include <array>
#include <vector>
#include <easylogging++.h>
#include "settings.h"
#include "setting_value.h"
#include "../utils/setup.h"
#include "specific_setting_value.h"
namespace settings
{
template <typename Enum, int ArraySize, typename Value>
void verifySettings( std::array<Value, ArraySize> v ) noexcept
{
for ( int settingIndex = 0; settingIndex < ( ArraySize - 1 );
++settingIndex )
{
// Having the enum values be in the correct location makes getting a
// value O(1) instead of O(n) due to array lookup vs traversal
// Also ensures that there are no mising values
const auto currentSetting
= v[static_cast<std::size_t>( settingIndex )].setting();
const auto settingCorrectlyLocated
= currentSetting == static_cast<Enum>( settingIndex );
if ( !settingCorrectlyLocated )
{
const auto enumName = typeid( Enum ).name();
const auto enumSettingNumber = static_cast<int>( currentSetting );
LOG( ERROR ) << enumName << ": at index '" << settingIndex
<< "' is incorrect. Current value: '"
<< enumSettingNumber
<< "'. Exiting "
"with error code "
<< ReturnErrorCode::SETTING_INCORRECT_INDEX << ".";
exit( ReturnErrorCode::SETTING_INCORRECT_INDEX );
}
}
LOG( DEBUG ) << "Settings for '" << typeid( Enum ).name() << "' verified.";
}
class SettingsController
{
public:
SettingsController()
{
verifySettings<BoolSetting, boolSettingSize>( m_boolSettings );
verifySettings<DoubleSetting, doubleSettingSize>( m_doubleSettings );
verifySettings<StringSetting, stringSettingsSize>( m_stringSettings );
verifySettings<IntSetting, intSettingsSize>( m_intSettings );
}
std::string getSettingsFileName() const noexcept
{
return getQSettings().fileName().toStdString();
}
void saveChangedSettings()
{
if ( m_changedSettings.empty() )
{
return;
}
for ( const auto setting : m_changedSettings )
{
setting->saveValue();
}
m_changedSettings.clear();
}
void saveAllSettings()
{
for ( auto& setting : m_boolSettings )
{
setting.saveValue();
}
}
template <typename ReturnType, typename Setting>
[[nodiscard]] ReturnType getSetting( const Setting setting ) const noexcept
{
const auto index = static_cast<std::size_t>( setting );
if constexpr ( std::is_same<Setting, BoolSetting>::value )
{
return m_boolSettings[index].value();
}
else if constexpr ( std::is_same<Setting, DoubleSetting>::value )
{
return m_doubleSettings[index].value();
}
else if constexpr ( std::is_same<Setting, IntSetting>::value )
{
return m_intSettings[index].value();
}
else if constexpr ( std::is_same<Setting, StringSetting>::value )
{
return m_stringSettings[index].value();
}
}
template <typename Setting, typename Type>
void setSetting( const Setting setting, const Type value ) noexcept
{
const auto index = static_cast<std::size_t>( setting );
if constexpr ( std::is_same<Setting, BoolSetting>::value )
{
auto& s = m_boolSettings[index];
s.setValue( value );
m_changedSettings.push_back( &s );
}
else if constexpr ( std::is_same<Setting, DoubleSetting>::value )
{
auto& s = m_doubleSettings[index];
s.setValue( value );
m_changedSettings.push_back( &s );
}
else if constexpr ( std::is_same<Setting, IntSetting>::value )
{
auto& s = m_intSettings[index];
s.setValue( value );
m_changedSettings.push_back( &s );
}
else if constexpr ( std::is_same<Setting, StringSetting>::value )
{
auto& s = m_stringSettings[index];
s.setValue( value );
m_changedSettings.push_back( &s );
}
}
private:
std::vector<SettingValue*> m_changedSettings{};
constexpr static auto boolSettingSize
= static_cast<int>( BoolSetting::LAST_ENUMERATOR ) + 1;
std::array<BoolSettingValue, boolSettingSize> m_boolSettings{
BoolSettingValue{ BoolSetting::PLAYSPACE_lockXToggle,
SettingCategory::Playspace,
QtInfo{ "lockXToggle" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_lockYToggle,
SettingCategory::Playspace,
QtInfo{ "lockYToggle" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_lockZToggle,
SettingCategory::Playspace,
QtInfo{ "lockZToggle" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_momentumSave,
SettingCategory::Playspace,
QtInfo{ "momentumSave" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_turnBindLeft,
SettingCategory::Playspace,
QtInfo{ "turnBindLeft" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_turnBindRight,
SettingCategory::Playspace,
QtInfo{ "turnBindRight" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_turnBounds,
SettingCategory::Playspace,
QtInfo{ "turnBounds" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_moveShortcutLeft,
SettingCategory::Playspace,
QtInfo{ "moveShortcutLeft" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_moveShortcutRight,
SettingCategory::Playspace,
QtInfo{ "moveShortcutRight" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_dragBounds,
SettingCategory::Playspace,
QtInfo{ "dragBounds" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_allowExternalEdits,
SettingCategory::Playspace,
QtInfo{ "allowExternalEdits" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_oldStyleMotion,
SettingCategory::Playspace,
QtInfo{ "oldStyleMotion" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_universeCenteredRotation,
SettingCategory::Playspace,
QtInfo{ "universeCenteredRotation" },
false },
BoolSettingValue{ BoolSetting::PLAYSPACE_enableSeatedMotion,
SettingCategory::Playspace,
QtInfo{ "enableSeatedMotion" },
false },
BoolSettingValue{ BoolSetting::APPLICATION_disableVersionCheck,
SettingCategory::Application,
QtInfo{ "disableVersionCheck" },
false },
BoolSettingValue{ BoolSetting::APPLICATION_previousShutdownSafe,
SettingCategory::Application,
QtInfo{ "previousShutdownSafe" },
true },
BoolSettingValue{ BoolSetting::VIDEO_brightnessEnabled,
SettingCategory::Video,
QtInfo{ "brightnessEnabled" },
false },
BoolSettingValue{ BoolSetting::VIDEO_isOverlayMethodActive,
SettingCategory::Video,
QtInfo{ "isOverlayMethodActive" },
false },
BoolSettingValue{ BoolSetting::VIDEO_colorOverlayEnabled,
SettingCategory::Video,
QtInfo{ "colorOverlayEnabled" },
false },
BoolSettingValue{ BoolSetting::CHAPERONE_disableChaperone,
SettingCategory::Chaperone,
QtInfo{ "disableChaperone" },
false },
};
constexpr static auto doubleSettingSize
= static_cast<int>( DoubleSetting::LAST_ENUMERATOR ) + 1;
std::array<DoubleSettingValue, doubleSettingSize> m_doubleSettings{
DoubleSettingValue{ DoubleSetting::UTILITY_desktopWidth,
SettingCategory::Utility,
QtInfo{ "desktopWidth" },
4.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_brightnessOpacityValue,
SettingCategory::Video,
QtInfo{ "brightnessOpacityValue" },
0.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_brightnessValue,
SettingCategory::Video,
QtInfo{ "brightnessValue" },
1.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorOverlayOpacity,
SettingCategory::Video,
QtInfo{ "colorOverlayOpacity" },
0.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorRed,
SettingCategory::Video,
QtInfo{ "colorRed" },
1.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorGreen,
SettingCategory::Video,
QtInfo{ "colorGreen" },
1.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorBlue,
SettingCategory::Video,
QtInfo{ "colorBlue" },
1.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorRedNew,
SettingCategory::Video,
QtInfo{ "colorRedNew" },
1.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorGreenNew,
SettingCategory::Video,
QtInfo{ "colorGreenNew" },
1.0 },
DoubleSettingValue{ DoubleSetting::VIDEO_colorBlueNew,
SettingCategory::Video,
QtInfo{ "colorBlueNew" },
1.0 },
DoubleSettingValue{ DoubleSetting::CHAPERONE_switchToBeginnerDistance,
SettingCategory::Video,
QtInfo{ "switchToBeginnerDistance" },
0.5 },
DoubleSettingValue{ DoubleSetting::CHAPERONE_hapticFeedbackDistance,
SettingCategory::Video,
QtInfo{ "hapticFeedbackDistance" },
0.5 },
DoubleSettingValue{ DoubleSetting::CHAPERONE_alarmSoundDistance,
SettingCategory::Video,
QtInfo{ "alarmSoundDistance" },
0.5 },
DoubleSettingValue{ DoubleSetting::CHAPERONE_showDashboardDistance,
SettingCategory::Video,
QtInfo{ "showDashboardDistance" },
0.5 },
DoubleSettingValue{ DoubleSetting::CHAPERONE_fadeDistanceRemembered,
SettingCategory::Chaperone,
QtInfo{ "fadeDistanceRemembered" },
0.5 },
};
constexpr static auto stringSettingsSize
= static_cast<int>( StringSetting::LAST_ENUMERATOR ) + 1;
constexpr static auto discordDefaultMuteKeybinding = "^>m";
std::array<StringSettingValue, stringSettingsSize> m_stringSettings{
StringSettingValue{ StringSetting::KEYBOARDSHORTCUT_keyboardOne,
SettingCategory::KeyboardShortcut,
QtInfo{ "keyboardOne" },
discordDefaultMuteKeybinding },
StringSettingValue{ StringSetting::KEYBOARDSHORTCUT_keyboardTwo,
SettingCategory::KeyboardShortcut,
QtInfo{ "keyboardTwo" },
discordDefaultMuteKeybinding },
StringSettingValue{ StringSetting::KEYBOARDSHORTCUT_keyboardThree,
SettingCategory::KeyboardShortcut,
QtInfo{ "keyboardThree" },
discordDefaultMuteKeybinding },
};
constexpr static auto intSettingsSize
= static_cast<int>( IntSetting::LAST_ENUMERATOR ) + 1;
std::array<IntSettingValue, intSettingsSize> m_intSettings{
IntSettingValue{ IntSetting::UTILITY_alarmHour,
SettingCategory::Utility,
QtInfo{ "alarmHour" },
0 },
IntSettingValue{ IntSetting::UTILITY_alarmMinute,
SettingCategory::Utility,
QtInfo{ "alarmMinute" },
0 },
};
};
} // namespace settings
+90
View File
@@ -0,0 +1,90 @@
#pragma once
#include <QSettings>
#include <string>
#include <type_traits>
#include "../overlaycontroller.h"
namespace settings
{
enum class SettingCategory
{
Utility,
KeyboardShortcut,
Playspace,
Application,
Video,
Chaperone,
ChaperoneProfiles,
};
struct QtInfo
{
const std::string settingName;
};
[[nodiscard]] QSettings& getQSettings()
{
static QSettings s( QSettings::IniFormat,
QSettings::UserScope,
application_strings::applicationOrganizationName,
application_strings::applicationName );
return s;
}
[[nodiscard]] std::string getQtCategoryName( const SettingCategory category )
{
switch ( category )
{
case SettingCategory::Utility:
return "utilitiesSettings";
case SettingCategory::KeyboardShortcut:
return "keyboardShortcuts";
case SettingCategory::Playspace:
return "playspaceSettings";
case SettingCategory::Application:
return "applicationSettings";
case SettingCategory::Video:
return "videoSettings";
case SettingCategory::Chaperone:
return "chaperoneSettings";
case SettingCategory::ChaperoneProfiles:
return "chaperoneProfiles";
}
return "no-value";
}
[[nodiscard]] QVariant getQtSetting( const SettingCategory category,
const std::string qtSettingName )
{
getQSettings().beginGroup( getQtCategoryName( category ).c_str() );
const auto v = getQSettings().value( qtSettingName.c_str() );
getQSettings().endGroup();
return v;
}
void saveQtSetting( const SettingCategory category,
const std::string qtSettingName,
const QVariant value )
{
getQSettings().beginGroup( getQtCategoryName( category ).c_str() );
getQSettings().setValue( qtSettingName.c_str(), value );
getQSettings().endGroup();
}
[[nodiscard]] bool isValidQVariant( const QVariant v,
const QMetaType::Type type )
{
const auto savedSettingIsValid
= v.isValid() && !v.isNull() && ( v.userType() == type );
if ( savedSettingIsValid )
{
return true;
}
return false;
}
} // namespace settings
+130
View File
@@ -0,0 +1,130 @@
#pragma once
#include <type_traits>
#include "setting_value.h"
#include "settings.h"
namespace settings
{
template <typename Value, typename Setting>
class SpecificSettingValue : public SettingValue
{
public:
SpecificSettingValue( const Setting setting,
const SettingCategory category,
const QtInfo qtInfo,
const Value defaultValue )
: SettingValue( category, qtInfo ), m_setting( setting ),
m_value( defaultValue )
{
// Only allow explicitly defined types to curtail unintended behavior
constexpr auto isBool = std::is_same<Value, bool>::value;
constexpr auto isDouble = std::is_same<Value, double>::value;
constexpr auto isString = std::is_same<Value, std::string>::value;
constexpr auto isInt = std::is_same<Value, int>::value;
static_assert( isBool || isDouble || isString || isInt );
const auto v = getQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName );
QMetaType::Type type;
if constexpr ( std::is_same<Value, bool>::value )
{
type = QMetaType::Bool;
}
else if constexpr ( std::is_same<Value, double>::value )
{
type = QMetaType::Double;
}
else if constexpr ( std::is_same<Value, std::string>::value )
{
type = QMetaType::QString;
}
else if constexpr ( std::is_same<Value, int>::value )
{
type = QMetaType::Int;
}
if ( isValidQVariant( v, type ) )
{
if constexpr ( std::is_same<Value, bool>::value )
{
m_value = v.toBool();
}
else if constexpr ( std::is_same<Value, double>::value )
{
m_value = v.toDouble();
}
else if constexpr ( std::is_same<Value, std::string>::value )
{
m_value = v.toString().toStdString();
}
else if constexpr ( std::is_same<Value, int>::value )
{
m_value = v.toInt();
}
}
else
{
// If setting doesn't exist, create it automatically
saveValue();
}
}
void setValue( const Value value ) noexcept
{
m_value = value;
}
[[nodiscard]] Value value() const noexcept
{
return m_value;
}
[[nodiscard]] Setting setting() const noexcept
{
return m_setting;
}
[[nodiscard]] SettingCategory category() const noexcept
{
return SettingValue::category();
}
[[nodiscard]] QtInfo qtInfo() const noexcept
{
return SettingValue::qtInfo();
}
void saveValue() override
{
if constexpr ( !std::is_same<Value, std::string>::value )
{
saveQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName,
m_value );
}
else
{
// Special case for std::string because it can't be auto
// converted to QVariant
saveQtSetting( SettingValue::category(),
SettingValue::qtInfo().settingName,
m_value.c_str() );
}
}
private:
const Setting m_setting;
Value m_value;
};
using BoolSettingValue = SpecificSettingValue<bool, BoolSetting>;
using DoubleSettingValue = SpecificSettingValue<double, DoubleSetting>;
using IntSettingValue = SpecificSettingValue<int, IntSetting>;
using StringSettingValue = SpecificSettingValue<std::string, StringSetting>;
} // namespace settings
+1
View File
@@ -17,6 +17,7 @@ enum ReturnErrorCode
SUCCESS = 0,
GENERAL_FAILURE = -1,
OPENVR_INIT_ERROR = -2,
SETTING_INCORRECT_INDEX = -3,
};
struct CommandLineOptions