commit
fca7d975fd
@ -0,0 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
#include "common/settings_common.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
BasicSetting::BasicSetting(Linkage& linkage, const std::string& name, enum Category category_,
|
||||
bool save_, bool runtime_modifiable_, u32 specialization_,
|
||||
BasicSetting* other_setting_)
|
||||
: label{name}, category{category_}, id{linkage.count}, save{save_},
|
||||
runtime_modifiable{runtime_modifiable_}, specialization{specialization_},
|
||||
other_setting{other_setting_} {
|
||||
linkage.by_category[category].push_back(this);
|
||||
linkage.count++;
|
||||
}
|
||||
|
||||
BasicSetting::~BasicSetting() = default;
|
||||
|
||||
std::string BasicSetting::ToStringGlobal() const {
|
||||
return this->ToString();
|
||||
}
|
||||
|
||||
bool BasicSetting::UsingGlobal() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void BasicSetting::SetGlobal(bool global) {}
|
||||
|
||||
bool BasicSetting::Save() const {
|
||||
return save;
|
||||
}
|
||||
|
||||
bool BasicSetting::RuntimeModfiable() const {
|
||||
return runtime_modifiable;
|
||||
}
|
||||
|
||||
Category BasicSetting::GetCategory() const {
|
||||
return category;
|
||||
}
|
||||
|
||||
u32 BasicSetting::Specialization() const {
|
||||
return specialization;
|
||||
}
|
||||
|
||||
BasicSetting* BasicSetting::PairedSetting() const {
|
||||
return other_setting;
|
||||
}
|
||||
|
||||
const std::string& BasicSetting::GetLabel() const {
|
||||
return label;
|
||||
}
|
||||
|
||||
Linkage::Linkage(u32 initial_count) : count{initial_count} {}
|
||||
Linkage::~Linkage() = default;
|
||||
|
||||
} // namespace Settings
|
@ -0,0 +1,256 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
enum class Category : u32 {
|
||||
Audio,
|
||||
Core,
|
||||
Cpu,
|
||||
CpuDebug,
|
||||
CpuUnsafe,
|
||||
Renderer,
|
||||
RendererAdvanced,
|
||||
RendererDebug,
|
||||
System,
|
||||
SystemAudio,
|
||||
DataStorage,
|
||||
Debugging,
|
||||
DebuggingGraphics,
|
||||
Miscellaneous,
|
||||
Network,
|
||||
WebService,
|
||||
AddOns,
|
||||
Controls,
|
||||
Ui,
|
||||
UiGeneral,
|
||||
UiLayout,
|
||||
UiGameList,
|
||||
Screenshots,
|
||||
Shortcuts,
|
||||
Multiplayer,
|
||||
Services,
|
||||
Paths,
|
||||
MaxEnum,
|
||||
};
|
||||
|
||||
constexpr u8 SpecializationTypeMask = 0xf;
|
||||
constexpr u8 SpecializationAttributeMask = 0xf0;
|
||||
constexpr u8 SpecializationAttributeOffset = 4;
|
||||
|
||||
// Scalar and countable could have better names
|
||||
enum Specialization : u8 {
|
||||
Default = 0,
|
||||
Time = 1, // Duration or specific moment in time
|
||||
Hex = 2, // Hexadecimal number
|
||||
List = 3, // Setting has specific members
|
||||
RuntimeList = 4, // Members of the list are determined during runtime
|
||||
Scalar = 5, // Values are continuous
|
||||
Countable = 6, // Can be stepped through
|
||||
Paired = 7, // Another setting is associated with this setting
|
||||
|
||||
Percentage = (1 << SpecializationAttributeOffset), // Should be represented as a percentage
|
||||
};
|
||||
|
||||
class BasicSetting;
|
||||
|
||||
class Linkage {
|
||||
public:
|
||||
explicit Linkage(u32 initial_count = 0);
|
||||
~Linkage();
|
||||
std::map<Category, std::vector<BasicSetting*>> by_category{};
|
||||
std::vector<std::function<void()>> restore_functions{};
|
||||
u32 count;
|
||||
};
|
||||
|
||||
/**
|
||||
* BasicSetting is an abstract class that only keeps track of metadata. The string methods are
|
||||
* available to get data values out.
|
||||
*/
|
||||
class BasicSetting {
|
||||
protected:
|
||||
explicit BasicSetting(Linkage& linkage, const std::string& name, Category category_, bool save_,
|
||||
bool runtime_modifiable_, u32 specialization,
|
||||
BasicSetting* other_setting);
|
||||
|
||||
public:
|
||||
virtual ~BasicSetting();
|
||||
|
||||
/*
|
||||
* Data retrieval
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns a string representation of the internal data. If the Setting is Switchable, it
|
||||
* respects the internal global state: it is based on GetValue().
|
||||
*
|
||||
* @returns A string representation of the internal data.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string ToString() const = 0;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the global version of internal data. If the Setting is
|
||||
* not Switchable, it behaves like ToString.
|
||||
*
|
||||
* @returns A string representation of the global version of internal data.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string ToStringGlobal() const;
|
||||
|
||||
/**
|
||||
* @returns A string representation of the Setting's default value.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string DefaultToString() const = 0;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the minimum value of the setting. If the Setting is not
|
||||
* ranged, the string represents the default initialization of the data type.
|
||||
*
|
||||
* @returns A string representation of the minimum value of the setting.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string MinVal() const = 0;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the maximum value of the setting. If the Setting is not
|
||||
* ranged, the string represents the default initialization of the data type.
|
||||
*
|
||||
* @returns A string representation of the maximum value of the setting.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string MaxVal() const = 0;
|
||||
|
||||
/**
|
||||
* Takes a string input, converts it to the internal data type if necessary, and then runs
|
||||
* SetValue with it.
|
||||
*
|
||||
* @param load String of the input data.
|
||||
*/
|
||||
virtual void LoadString(const std::string& load) = 0;
|
||||
|
||||
/**
|
||||
* Returns a string representation of the data. If the data is an enum, it returns a string of
|
||||
* the enum value. If the internal data type is not an enum, this is equivalent to ToString.
|
||||
*
|
||||
* e.g. renderer_backend.Canonicalize() == "OpenGL"
|
||||
*
|
||||
* @returns Canonicalized string representation of the internal data
|
||||
*/
|
||||
[[nodiscard]] virtual std::string Canonicalize() const = 0;
|
||||
|
||||
/*
|
||||
* Metadata
|
||||
*/
|
||||
|
||||
/**
|
||||
* @returns A unique identifier for the Setting's internal data type.
|
||||
*/
|
||||
[[nodiscard]] virtual std::type_index TypeId() const = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the Setting's internal data type is an enum.
|
||||
*
|
||||
* @returns True if the Setting's internal data type is an enum
|
||||
*/
|
||||
[[nodiscard]] virtual constexpr bool IsEnum() const = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the current setting is Switchable.
|
||||
*
|
||||
* @returns If the setting is a SwitchableSetting
|
||||
*/
|
||||
[[nodiscard]] virtual constexpr bool Switchable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true to suggest that a frontend can read or write the setting to a configuration
|
||||
* file.
|
||||
*
|
||||
* @returns The save preference
|
||||
*/
|
||||
[[nodiscard]] bool Save() const;
|
||||
|
||||
/**
|
||||
* @returns true if the current setting can be changed while the guest is running.
|
||||
*/
|
||||
[[nodiscard]] bool RuntimeModfiable() const;
|
||||
|
||||
/**
|
||||
* @returns A unique number corresponding to the setting.
|
||||
*/
|
||||
[[nodiscard]] constexpr u32 Id() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setting's category AKA INI group.
|
||||
*
|
||||
* @returns The setting's category
|
||||
*/
|
||||
[[nodiscard]] Category GetCategory() const;
|
||||
|
||||
/**
|
||||
* @returns Extra metadata for data representation in frontend implementations.
|
||||
*/
|
||||
[[nodiscard]] u32 Specialization() const;
|
||||
|
||||
/**
|
||||
* @returns Another BasicSetting if one is paired, or nullptr otherwise.
|
||||
*/
|
||||
[[nodiscard]] BasicSetting* PairedSetting() const;
|
||||
|
||||
/**
|
||||
* Returns the label this setting was created with.
|
||||
*
|
||||
* @returns A reference to the label
|
||||
*/
|
||||
[[nodiscard]] const std::string& GetLabel() const;
|
||||
|
||||
/**
|
||||
* @returns If the Setting checks input values for valid ranges.
|
||||
*/
|
||||
[[nodiscard]] virtual constexpr bool Ranged() const = 0;
|
||||
|
||||
/**
|
||||
* @returns The index of the enum if the underlying setting type is an enum, else max of u32.
|
||||
*/
|
||||
[[nodiscard]] virtual constexpr u32 EnumIndex() const = 0;
|
||||
|
||||
/*
|
||||
* Switchable settings
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sets a setting's global state. True means use the normal setting, false to use a custom
|
||||
* value. Has no effect if the Setting is not Switchable.
|
||||
*
|
||||
* @param global The desired state
|
||||
*/
|
||||
virtual void SetGlobal(bool global);
|
||||
|
||||
/**
|
||||
* Returns true if the setting is using the normal setting value. Always true if the setting is
|
||||
* not Switchable.
|
||||
*
|
||||
* @returns The Setting's global state
|
||||
*/
|
||||
[[nodiscard]] virtual bool UsingGlobal() const;
|
||||
|
||||
private:
|
||||
const std::string label; ///< The setting's label
|
||||
const Category category; ///< The setting's category AKA INI group
|
||||
const u32 id; ///< Unique integer for the setting
|
||||
const bool save; ///< Suggests if the setting should be saved and read to a frontend config
|
||||
const bool
|
||||
runtime_modifiable; ///< Suggests if the setting can be modified while a guest is running
|
||||
const u32 specialization; ///< Extra data to identify representation of a setting
|
||||
BasicSetting* const other_setting; ///< A paired setting
|
||||
};
|
||||
|
||||
} // namespace Settings
|
@ -0,0 +1,214 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
template <typename T>
|
||||
struct EnumMetadata {
|
||||
static constexpr std::vector<std::pair<std::string, T>> Canonicalizations();
|
||||
static constexpr u32 Index();
|
||||
};
|
||||
|
||||
#define PAIR_45(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_46(N, __VA_ARGS__))
|
||||
#define PAIR_44(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_45(N, __VA_ARGS__))
|
||||
#define PAIR_43(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_44(N, __VA_ARGS__))
|
||||
#define PAIR_42(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_43(N, __VA_ARGS__))
|
||||
#define PAIR_41(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_42(N, __VA_ARGS__))
|
||||
#define PAIR_40(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_41(N, __VA_ARGS__))
|
||||
#define PAIR_39(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_40(N, __VA_ARGS__))
|
||||
#define PAIR_38(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_39(N, __VA_ARGS__))
|
||||
#define PAIR_37(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_38(N, __VA_ARGS__))
|
||||
#define PAIR_36(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_37(N, __VA_ARGS__))
|
||||
#define PAIR_35(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_36(N, __VA_ARGS__))
|
||||
#define PAIR_34(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_35(N, __VA_ARGS__))
|
||||
#define PAIR_33(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_34(N, __VA_ARGS__))
|
||||
#define PAIR_32(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_33(N, __VA_ARGS__))
|
||||
#define PAIR_31(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_32(N, __VA_ARGS__))
|
||||
#define PAIR_30(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_31(N, __VA_ARGS__))
|
||||
#define PAIR_29(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_30(N, __VA_ARGS__))
|
||||
#define PAIR_28(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_29(N, __VA_ARGS__))
|
||||
#define PAIR_27(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_28(N, __VA_ARGS__))
|
||||
#define PAIR_26(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_27(N, __VA_ARGS__))
|
||||
#define PAIR_25(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_26(N, __VA_ARGS__))
|
||||
#define PAIR_24(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_25(N, __VA_ARGS__))
|
||||
#define PAIR_23(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_24(N, __VA_ARGS__))
|
||||
#define PAIR_22(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_23(N, __VA_ARGS__))
|
||||
#define PAIR_21(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_22(N, __VA_ARGS__))
|
||||
#define PAIR_20(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_21(N, __VA_ARGS__))
|
||||
#define PAIR_19(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_20(N, __VA_ARGS__))
|
||||
#define PAIR_18(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_19(N, __VA_ARGS__))
|
||||
#define PAIR_17(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_18(N, __VA_ARGS__))
|
||||
#define PAIR_16(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_17(N, __VA_ARGS__))
|
||||
#define PAIR_15(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_16(N, __VA_ARGS__))
|
||||
#define PAIR_14(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_15(N, __VA_ARGS__))
|
||||
#define PAIR_13(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_14(N, __VA_ARGS__))
|
||||
#define PAIR_12(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_13(N, __VA_ARGS__))
|
||||
#define PAIR_11(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_12(N, __VA_ARGS__))
|
||||
#define PAIR_10(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_11(N, __VA_ARGS__))
|
||||
#define PAIR_9(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_10(N, __VA_ARGS__))
|
||||
#define PAIR_8(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_9(N, __VA_ARGS__))
|
||||
#define PAIR_7(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_8(N, __VA_ARGS__))
|
||||
#define PAIR_6(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_7(N, __VA_ARGS__))
|
||||
#define PAIR_5(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_6(N, __VA_ARGS__))
|
||||
#define PAIR_4(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_5(N, __VA_ARGS__))
|
||||
#define PAIR_3(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_4(N, __VA_ARGS__))
|
||||
#define PAIR_2(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_3(N, __VA_ARGS__))
|
||||
#define PAIR_1(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_2(N, __VA_ARGS__))
|
||||
#define PAIR(N, X, ...) {#X, N::X} __VA_OPT__(, PAIR_1(N, __VA_ARGS__))
|
||||
|
||||
#define ENUM(NAME, ...) \
|
||||
enum class NAME : u32 { __VA_ARGS__ }; \
|
||||
template <> \
|
||||
constexpr std::vector<std::pair<std::string, NAME>> EnumMetadata<NAME>::Canonicalizations() { \
|
||||
return {PAIR(NAME, __VA_ARGS__)}; \
|
||||
} \
|
||||
template <> \
|
||||
constexpr u32 EnumMetadata<NAME>::Index() { \
|
||||
return __COUNTER__; \
|
||||
}
|
||||
|
||||
// AudioEngine must be specified discretely due to having existing but slightly different
|
||||
// canonicalizations
|
||||
// TODO (lat9nq): Remove explicit definition of AudioEngine/sink_id
|
||||
enum class AudioEngine : u32 {
|
||||
Auto,
|
||||
Cubeb,
|
||||
Sdl2,
|
||||
Null,
|
||||
};
|
||||
|
||||
template <>
|
||||
constexpr std::vector<std::pair<std::string, AudioEngine>>
|
||||
EnumMetadata<AudioEngine>::Canonicalizations() {
|
||||
return {
|
||||
{"auto", AudioEngine::Auto},
|
||||
{"cubeb", AudioEngine::Cubeb},
|
||||
{"sdl2", AudioEngine::Sdl2},
|
||||
{"null", AudioEngine::Null},
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr u32 EnumMetadata<AudioEngine>::Index() {
|
||||
// This is just a sufficiently large number that is more than the number of other enums declared
|
||||
// here
|
||||
return 100;
|
||||
}
|
||||
|
||||
ENUM(AudioMode, Mono, Stereo, Surround);
|
||||
|
||||
ENUM(Language, Japanese, EnglishAmerican, French, German, Italian, Spanish, Chinese, Korean, Dutch,
|
||||
Portuguese, Russian, Taiwanese, EnglishBritish, FrenchCanadian, SpanishLatin,
|
||||
ChineseSimplified, ChineseTraditional, PortugueseBrazilian);
|
||||
|
||||
ENUM(Region, Japan, Usa, Europe, Australia, China, Korea, Taiwan);
|
||||
|
||||
ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt, Gb, GbEire, Gmt,
|
||||
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
|
||||
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
|
||||
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
|
||||
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
|
||||
|
||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||
|
||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
|
||||
ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
|
||||
|
||||
ENUM(RendererBackend, OpenGL, Vulkan, Null);
|
||||
|
||||
ENUM(ShaderBackend, Glsl, Glasm, SpirV);
|
||||
|
||||
ENUM(GpuAccuracy, Normal, High, Extreme);
|
||||
|
||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
||||
|
||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||
|
||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||
|
||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||
|
||||
ENUM(ResolutionSetup, Res1_2X, Res3_4X, Res1X, Res3_2X, Res2X, Res3X, Res4X, Res5X, Res6X, Res7X,
|
||||
Res8X);
|
||||
|
||||
ENUM(ScalingFilter, NearestNeighbor, Bilinear, Bicubic, Gaussian, ScaleForce, Fsr, MaxEnum);
|
||||
|
||||
ENUM(AntiAliasing, None, Fxaa, Smaa, MaxEnum);
|
||||
|
||||
ENUM(AspectRatio, R16_9, R4_3, R21_9, R16_10, Stretch);
|
||||
|
||||
template <typename Type>
|
||||
constexpr std::string CanonicalizeEnum(Type id) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
for (auto& [name, value] : group) {
|
||||
if (value == id) {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
constexpr Type ToEnum(const std::string& canonicalization) {
|
||||
const auto group = EnumMetadata<Type>::Canonicalizations();
|
||||
for (auto& [name, value] : group) {
|
||||
if (name == canonicalization) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
} // namespace Settings
|
||||
|
||||
#undef ENUM
|
||||
#undef PAIR
|
||||
#undef PAIR_1
|
||||
#undef PAIR_2
|
||||
#undef PAIR_3
|
||||
#undef PAIR_4
|
||||
#undef PAIR_5
|
||||
#undef PAIR_6
|
||||
#undef PAIR_7
|
||||
#undef PAIR_8
|
||||
#undef PAIR_9
|
||||
#undef PAIR_10
|
||||
#undef PAIR_12
|
||||
#undef PAIR_13
|
||||
#undef PAIR_14
|
||||
#undef PAIR_15
|
||||
#undef PAIR_16
|
||||
#undef PAIR_17
|
||||
#undef PAIR_18
|
||||
#undef PAIR_19
|
||||
#undef PAIR_20
|
||||
#undef PAIR_22
|
||||
#undef PAIR_23
|
||||
#undef PAIR_24
|
||||
#undef PAIR_25
|
||||
#undef PAIR_26
|
||||
#undef PAIR_27
|
||||
#undef PAIR_28
|
||||
#undef PAIR_29
|
||||
#undef PAIR_30
|
||||
#undef PAIR_32
|
||||
#undef PAIR_33
|
||||
#undef PAIR_34
|
||||
#undef PAIR_35
|
||||
#undef PAIR_36
|
||||
#undef PAIR_37
|
||||
#undef PAIR_38
|
||||
#undef PAIR_39
|
||||
#undef PAIR_40
|
||||
#undef PAIR_42
|
||||
#undef PAIR_43
|
||||
#undef PAIR_44
|
||||
#undef PAIR_45
|
@ -0,0 +1,394 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings_common.h"
|
||||
#include "common/settings_enums.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
/** The Setting class is a simple resource manager. It defines a label and default value
|
||||
* alongside the actual value of the setting for simpler and less-error prone use with frontend
|
||||
* configurations. Specifying a default value and label is required. A minimum and maximum range
|
||||
* can be specified for sanitization.
|
||||
*/
|
||||
template <typename Type, bool ranged = false>
|
||||
class Setting : public BasicSetting {
|
||||
protected:
|
||||
Setting() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Sets a default value, label, and setting value.
|
||||
*
|
||||
* @param linkage Setting registry
|
||||
* @param default_val Initial value of the setting, and default value of the setting
|
||||
* @param name Label for the setting
|
||||
* @param category_ Category of the setting AKA INI group
|
||||
* @param specialization_ Suggestion for how frontend implementations represent this in a config
|
||||
* @param save_ Suggests that this should or should not be saved to a frontend config file
|
||||
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
|
||||
* @param other_setting_ A second Setting to associate to this one in metadata
|
||||
*/
|
||||
explicit Setting(Linkage& linkage, const Type& default_val, const std::string& name,
|
||||
Category category_, u32 specialization_ = Specialization::Default,
|
||||
bool save_ = true, bool runtime_modifiable_ = false,
|
||||
BasicSetting* other_setting_ = nullptr)
|
||||
requires(!ranged)
|
||||
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
|
||||
other_setting_),
|
||||
value{default_val}, default_value{default_val} {}
|
||||
virtual ~Setting() = default;
|
||||
|
||||
/**
|
||||
* Sets a default value, minimum value, maximum value, and label.
|
||||
*
|
||||
* @param linkage Setting registry
|
||||
* @param default_val Initial value of the setting, and default value of the setting
|
||||
* @param min_val Sets the minimum allowed value of the setting
|
||||
* @param max_val Sets the maximum allowed value of the setting
|
||||
* @param name Label for the setting
|
||||
* @param category_ Category of the setting AKA INI group
|
||||
* @param specialization_ Suggestion for how frontend implementations represent this in a config
|
||||
* @param save_ Suggests that this should or should not be saved to a frontend config file
|
||||
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
|
||||
* @param other_setting_ A second Setting to associate to this one in metadata
|
||||
*/
|
||||
explicit Setting(Linkage& linkage, const Type& default_val, const Type& min_val,
|
||||
const Type& max_val, const std::string& name, Category category_,
|
||||
u32 specialization_ = Specialization::Default, bool save_ = true,
|
||||
bool runtime_modifiable_ = false, BasicSetting* other_setting_ = nullptr)
|
||||
requires(ranged)
|
||||
: BasicSetting(linkage, name, category_, save_, runtime_modifiable_, specialization_,
|
||||
other_setting_),
|
||||
value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val} {}
|
||||
|
||||
/**
|
||||
* Returns a reference to the setting's value.
|
||||
*
|
||||
* @returns A reference to the setting
|
||||
*/
|
||||
[[nodiscard]] virtual const Type& GetValue() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the setting to the given value.
|
||||
*
|
||||
* @param val The desired value
|
||||
*/
|
||||
virtual void SetValue(const Type& val) {
|
||||
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
|
||||
std::swap(value, temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value that this setting was created with.
|
||||
*
|
||||
* @returns A reference to the default value
|
||||
*/
|
||||
[[nodiscard]] const Type& GetDefault() const {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool IsEnum() const override {
|
||||
return std::is_enum_v<Type>;
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]] std::string ToString(const Type& value_) const {
|
||||
if constexpr (std::is_same_v<Type, std::string>) {
|
||||
return value_;
|
||||
} else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
|
||||
return value_.has_value() ? std::to_string(*value_) : "none";
|
||||
} else if constexpr (std::is_same_v<Type, bool>) {
|
||||
return value_ ? "true" : "false";
|
||||
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
|
||||
// Compatibility with old AudioEngine setting being a string
|
||||
return CanonicalizeEnum(value_);
|
||||
} else {
|
||||
return std::to_string(static_cast<u64>(value_));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Converts the value of the setting to a std::string. Respects the global state if the setting
|
||||
* has one.
|
||||
*
|
||||
* @returns The current setting as a std::string
|
||||
*/
|
||||
[[nodiscard]] std::string ToString() const override {
|
||||
return ToString(this->GetValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value of the setting as a std::string.
|
||||
*
|
||||
* @returns The default value as a string.
|
||||
*/
|
||||
[[nodiscard]] std::string DefaultToString() const override {
|
||||
return ToString(default_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a value to the setting.
|
||||
*
|
||||
* @param val The desired setting value
|
||||
*
|
||||
* @returns A reference to the setting
|
||||
*/
|
||||
virtual const Type& operator=(const Type& val) {
|
||||
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
|
||||
std::swap(value, temp);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the setting.
|
||||
*
|
||||
* @returns A reference to the setting
|
||||
*/
|
||||
explicit virtual operator const Type&() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given value to the Setting's type of value. Uses SetValue to enter the setting,
|
||||
* thus respecting its constraints.
|
||||
*
|
||||
* @param input The desired value
|
||||
*/
|
||||
void LoadString(const std::string& input) override final {
|
||||
if (input.empty()) {
|
||||
this->SetValue(this->GetDefault());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if constexpr (std::is_same_v<Type, std::string>) {
|
||||
this->SetValue(input);
|
||||
} else if constexpr (std::is_same_v<Type, std::optional<u32>>) {
|
||||
this->SetValue(static_cast<u32>(std::stoul(input)));
|
||||
} else if constexpr (std::is_same_v<Type, bool>) {
|
||||
this->SetValue(input == "true");
|
||||
} else if constexpr (std::is_same_v<Type, AudioEngine>) {
|
||||
this->SetValue(ToEnum<Type>(input));
|
||||
} else {
|
||||
this->SetValue(static_cast<Type>(std::stoll(input)));
|
||||
}
|
||||
} catch (std::invalid_argument&) {
|
||||
this->SetValue(this->GetDefault());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string constexpr Canonicalize() const override final {
|
||||
if constexpr (std::is_enum_v<Type>) {
|
||||
return CanonicalizeEnum(this->GetValue());
|
||||
} else {
|
||||
return ToString(this->GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives us another way to identify the setting without having to go through a string.
|
||||
*
|
||||
* @returns the type_index of the setting's type
|
||||
*/
|
||||
[[nodiscard]] std::type_index TypeId() const override final {
|
||||
return std::type_index(typeid(Type));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u32 EnumIndex() const override final {
|
||||
if constexpr (std::is_enum_v<Type>) {
|
||||
return EnumMetadata<Type>::Index();
|
||||
} else {
|
||||
return std::numeric_limits<u32>::max();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MinVal() const override final {
|
||||
return this->ToString(minimum);
|
||||
}
|
||||
[[nodiscard]] std::string MaxVal() const override final {
|
||||
return this->ToString(maximum);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool Ranged() const override {
|
||||
return ranged;
|
||||
}
|
||||
|
||||
protected:
|
||||
Type value{}; ///< The setting
|
||||
const Type default_value{}; ///< The default value
|
||||
const Type maximum{}; ///< Maximum allowed value of the setting
|
||||
const Type minimum{}; ///< Minimum allowed value of the setting
|
||||
};
|
||||
|
||||
/**
|
||||
* The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a
|
||||
* custom setting to switch to when a guest application specifically requires it. The effect is that
|
||||
* other components of the emulator can access the setting's intended value without any need for the
|
||||
* component to ask whether the custom or global setting is needed at the moment.
|
||||
*
|
||||
* By default, the global setting is used.
|
||||
*/
|
||||
template <typename Type, bool ranged = false>
|
||||
class SwitchableSetting : virtual public Setting<Type, ranged> {
|
||||
public:
|
||||
/**
|
||||
* Sets a default value, label, and setting value.
|
||||
*
|
||||
* @param linkage Setting registry
|
||||
* @param default_val Initial value of the setting, and default value of the setting
|
||||
* @param name Label for the setting
|
||||
* @param category_ Category of the setting AKA INI group
|
||||
* @param specialization_ Suggestion for how frontend implementations represent this in a config
|
||||
* @param save_ Suggests that this should or should not be saved to a frontend config file
|
||||
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
|
||||
* @param other_setting_ A second Setting to associate to this one in metadata
|
||||
*/
|
||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const std::string& name,
|
||||
Category category_, u32 specialization_ = Specialization::Default,
|
||||
bool save_ = true, bool runtime_modifiable_ = false,
|
||||
BasicSetting* other_setting_ = nullptr)
|
||||
requires(!ranged)
|
||||
: Setting<Type, false>{
|
||||
linkage, default_val, name, category_, specialization_,
|
||||
save_, runtime_modifiable_, other_setting_} {
|
||||
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
||||
}
|
||||
virtual ~SwitchableSetting() = default;
|
||||
|
||||
/**
|
||||
* Sets a default value, minimum value, maximum value, and label.
|
||||
*
|
||||
* @param linkage Setting registry
|
||||
* @param default_val Initial value of the setting, and default value of the setting
|
||||
* @param min_val Sets the minimum allowed value of the setting
|
||||
* @param max_val Sets the maximum allowed value of the setting
|
||||
* @param name Label for the setting
|
||||
* @param category_ Category of the setting AKA INI group
|
||||
* @param specialization_ Suggestion for how frontend implementations represent this in a config
|
||||
* @param save_ Suggests that this should or should not be saved to a frontend config file
|
||||
* @param runtime_modifiable_ Suggests whether this is modifiable while a guest is loaded
|
||||
* @param other_setting_ A second Setting to associate to this one in metadata
|
||||
*/
|
||||
explicit SwitchableSetting(Linkage& linkage, const Type& default_val, const Type& min_val,
|
||||
const Type& max_val, const std::string& name, Category category_,
|
||||
u32 specialization_ = Specialization::Default, bool save_ = true,
|
||||
bool runtime_modifiable_ = false,
|
||||
BasicSetting* other_setting_ = nullptr)
|
||||
requires(ranged)
|
||||
: Setting<Type, true>{linkage, default_val, min_val,
|
||||
max_val, name, category_,
|
||||
specialization_, save_, runtime_modifiable_,
|
||||
other_setting_} {
|
||||
linkage.restore_functions.emplace_back([this]() { this->SetGlobal(true); });
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells this setting to represent either the global or custom setting when other member
|
||||
* functions are used.
|
||||
*
|
||||
* @param to_global Whether to use the global or custom setting.
|
||||
*/
|
||||
void SetGlobal(bool to_global) override final {
|
||||
use_global = to_global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this setting is using the global setting or not.
|
||||
*
|
||||
* @returns The global state
|
||||
*/
|
||||
[[nodiscard]] bool UsingGlobal() const override final {
|
||||
return use_global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either the global or custom setting depending on the values of this setting's global
|
||||
* state or if the global value was specifically requested.
|
||||
*
|
||||
* @param need_global Request global value regardless of setting's state; defaults to false
|
||||
*
|
||||
* @returns The required value of the setting
|
||||
*/
|
||||
[[nodiscard]] const Type& GetValue() const override final {
|
||||
if (use_global) {
|
||||
return this->value;
|
||||
}
|
||||
return custom;
|
||||
}
|
||||
[[nodiscard]] const Type& GetValue(bool need_global) const {
|
||||
if (use_global || need_global) {
|
||||
return this->value;
|
||||
}
|
||||
return custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current setting value depending on the global state.
|
||||
*
|
||||
* @param val The new value
|
||||
*/
|
||||
void SetValue(const Type& val) override final {
|
||||
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
|
||||
if (use_global) {
|
||||
std::swap(this->value, temp);
|
||||
} else {
|
||||
std::swap(custom, temp);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool Switchable() const override final {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string ToStringGlobal() const override final {
|
||||
return this->ToString(this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the current setting value depending on the global state.
|
||||
*
|
||||
* @param val The new value
|
||||
*
|
||||
* @returns A reference to the current setting value
|
||||
*/
|
||||
const Type& operator=(const Type& val) override final {
|
||||
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
|
||||
if (use_global) {
|
||||
std::swap(this->value, temp);
|
||||
return this->value;
|
||||
}
|
||||
std::swap(custom, temp);
|
||||
return custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current setting value depending on the global state.
|
||||
*
|
||||
* @returns A reference to the current setting value
|
||||
*/
|
||||
explicit operator const Type&() const override final {
|
||||
if (use_global) {
|
||||
return this->value;
|
||||
}
|
||||
return custom;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool use_global{true}; ///< The setting's global state
|
||||
Type custom{}; ///< The custom value of the setting
|
||||
};
|
||||
|
||||
} // namespace Settings
|
File diff suppressed because it is too large
Load Diff
@ -1,104 +1,19 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include "common/settings.h"
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "yuzu/configuration/configuration_shared.h"
|
||||
#include "yuzu/configuration/configure_per_game.h"
|
||||
|
||||
void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting,
|
||||
const QCheckBox* checkbox,
|
||||
const CheckState& tracker) {
|
||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||
setting->SetValue(checkbox->checkState());
|
||||
} else if (!Settings::IsConfiguringGlobal()) {
|
||||
if (tracker == CheckState::Global) {
|
||||
setting->SetGlobal(true);
|
||||
} else {
|
||||
setting->SetGlobal(false);
|
||||
setting->SetValue(checkbox->checkState());
|
||||
}
|
||||
namespace ConfigurationShared {
|
||||
|
||||
Tab::Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent) : QWidget(parent) {
|
||||
if (group != nullptr) {
|
||||
group->push_back(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
|
||||
const Settings::SwitchableSetting<bool>* setting) {
|
||||
if (setting->UsingGlobal()) {
|
||||
checkbox->setCheckState(Qt::PartiallyChecked);
|
||||
} else {
|
||||
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
Tab::~Tab() = default;
|
||||
|
||||
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
|
||||
if (highlighted) {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }")
|
||||
.arg(widget->objectName()));
|
||||
} else {
|
||||
widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }")
|
||||
.arg(widget->objectName()));
|
||||
}
|
||||
widget->show();
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox,
|
||||
const Settings::SwitchableSetting<bool>& setting,
|
||||
CheckState& tracker) {
|
||||
if (setting.UsingGlobal()) {
|
||||
tracker = CheckState::Global;
|
||||
} else {
|
||||
tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off;
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(setting.GetValue(true));
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state,
|
||||
bool global_state, CheckState& tracker) {
|
||||
if (global) {
|
||||
tracker = CheckState::Global;
|
||||
} else {
|
||||
tracker = (state == global_state) ? CheckState::On : CheckState::Off;
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] {
|
||||
tracker = static_cast<CheckState>((static_cast<int>(tracker) + 1) %
|
||||
static_cast<int>(CheckState::Count));
|
||||
if (tracker == CheckState::Global) {
|
||||
checkbox->setChecked(global_state);
|
||||
}
|
||||
SetHighlight(checkbox, tracker != CheckState::Global);
|
||||
});
|
||||
}
|
||||
|
||||
void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) {
|
||||
InsertGlobalItem(combobox, global);
|
||||
QObject::connect(combobox, qOverload<int>(&QComboBox::activated), target,
|
||||
[target](int index) { SetHighlight(target, index != 0); });
|
||||
}
|
||||
|
||||
void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) {
|
||||
const QString use_global_text =
|
||||
ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index));
|
||||
combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text);
|
||||
combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX);
|
||||
}
|
||||
|
||||
int ConfigurationShared::GetComboboxIndex(int global_setting_index, const QComboBox* combobox) {
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
return combobox->currentIndex();
|
||||
}
|
||||
if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
return global_setting_index;
|
||||
}
|
||||
return combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET;
|
||||
}
|
||||
} // namespace ConfigurationShared
|
||||
|
@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include "common/common_types.h"
|
||||
|
||||
class QWidget;
|
||||
|
||||
namespace ConfigurationShared {
|
||||
using TranslationMap = std::map<u32, std::pair<QString, QString>>;
|
||||
using ComboboxTranslations = std::vector<std::pair<u32, QString>>;
|
||||
using ComboboxTranslationMap = std::map<u32, ComboboxTranslations>;
|
||||
|
||||
std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent);
|
||||
|
||||
std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent);
|
||||
|
||||
} // namespace ConfigurationShared
|
@ -0,0 +1,642 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "yuzu/configuration/shared_widget.h"
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QAbstractSlider>
|
||||
#include <QBoxLayout>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QDateTime>
|
||||
#include <QDateTimeEdit>
|
||||
#include <QIcon>
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QObject>
|
||||
#include <QPushButton>
|
||||
#include <QRegularExpression>
|
||||
#include <QSizePolicy>
|
||||
#include <QSlider>
|
||||
#include <QSpinBox>
|
||||
#include <QStyle>
|
||||
#include <QValidator>
|
||||
#include <QVariant>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qobjectdefs.h>
|
||||
#include <fmt/core.h>
|
||||
#include <qglobal.h>
|
||||
#include <qnamespace.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/settings_common.h"
|
||||
#include "yuzu/configuration/shared_translation.h"
|
||||
|
||||
namespace ConfigurationShared {
|
||||
|
||||
static int restore_button_count = 0;
|
||||
|
||||
static std::string RelevantDefault(const Settings::BasicSetting& setting) {
|
||||
return Settings::IsConfiguringGlobal() ? setting.DefaultToString() : setting.ToStringGlobal();
|
||||
}
|
||||
|
||||
static QString DefaultSuffix(QWidget* parent, Settings::BasicSetting& setting) {
|
||||
const auto tr = [parent](const char* text, const char* context) {
|
||||
return parent->tr(text, context);
|
||||
};
|
||||
|
||||
if ((setting.Specialization() & Settings::SpecializationAttributeMask) ==
|
||||
Settings::Specialization::Percentage) {
|
||||
std::string context{fmt::format("{} percentage (e.g. 50%)", setting.GetLabel())};
|
||||
return tr("%", context.c_str());
|
||||
}
|
||||
|
||||
return QStringLiteral("");
|
||||
}
|
||||
|
||||
QPushButton* Widget::CreateRestoreGlobalButton(bool using_global, QWidget* parent) {
|
||||
restore_button_count++;
|
||||
|
||||
QStyle* style = parent->style();
|
||||
QIcon* icon = new QIcon(style->standardIcon(QStyle::SP_LineEditClearButton));
|
||||
QPushButton* restore_button = new QPushButton(*icon, QStringLiteral(""), parent);
|
||||
restore_button->setObjectName(QStringLiteral("RestoreButton%1").arg(restore_button_count));
|
||||
restore_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
|
||||
// Workaround for dark theme causing min-width to be much larger than 0
|
||||
restore_button->setStyleSheet(
|
||||
QStringLiteral("QAbstractButton#%1 { min-width: 0px }").arg(restore_button->objectName()));
|
||||
|
||||
QSizePolicy sp_retain = restore_button->sizePolicy();
|
||||
sp_retain.setRetainSizeWhenHidden(true);
|
||||
restore_button->setSizePolicy(sp_retain);
|
||||
|
||||
restore_button->setEnabled(!using_global);
|
||||
restore_button->setVisible(!using_global);
|
||||
|
||||
return restore_button;
|
||||
}
|
||||
|
||||
QLabel* Widget::CreateLabel(const QString& text) {
|
||||
QLabel* qt_label = new QLabel(text, this->parent);
|
||||
qt_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
return qt_label;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
checkbox = new QCheckBox(label, this);
|
||||
checkbox->setCheckState(bool_setting->ToString() == "true" ? Qt::CheckState::Checked
|
||||
: Qt::CheckState::Unchecked);
|
||||
checkbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
if (!bool_setting->Save() && !Settings::IsConfiguringGlobal() && runtime_lock) {
|
||||
checkbox->setEnabled(false);
|
||||
}
|
||||
|
||||
serializer = [this]() {
|
||||
return checkbox->checkState() == Qt::CheckState::Checked ? "true" : "false";
|
||||
};
|
||||
|
||||
restore_func = [this, bool_setting]() {
|
||||
checkbox->setCheckState(RelevantDefault(*bool_setting) == "true" ? Qt::Checked
|
||||
: Qt::Unchecked);
|
||||
};
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QObject::connect(checkbox, &QCheckBox::clicked, [touch]() { touch(); });
|
||||
}
|
||||
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateCombobox(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
const auto type = setting.EnumIndex();
|
||||
|
||||
combobox = new QComboBox(this);
|
||||
combobox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
const ComboboxTranslations* enumeration{nullptr};
|
||||
if (combobox_enumerations.contains(type)) {
|
||||
enumeration = &combobox_enumerations.at(type);
|
||||
for (const auto& [id, name] : *enumeration) {
|
||||
combobox->addItem(name);
|
||||
}
|
||||
} else {
|
||||
return combobox;
|
||||
}
|
||||
|
||||
const auto find_index = [=](u32 value) -> int {
|
||||
for (u32 i = 0; i < enumeration->size(); i++) {
|
||||
if (enumeration->at(i).first == value) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
const u32 setting_value = std::stoi(setting.ToString());
|
||||
combobox->setCurrentIndex(find_index(setting_value));
|
||||
|
||||
serializer = [this, enumeration]() {
|
||||
int current = combobox->currentIndex();
|
||||
return std::to_string(enumeration->at(current).first);
|
||||
};
|
||||
|
||||
restore_func = [this, find_index]() {
|
||||
const u32 global_value = std::stoi(RelevantDefault(setting));
|
||||
combobox->setCurrentIndex(find_index(global_value));
|
||||
};
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QObject::connect(combobox, QOverload<int>::of(&QComboBox::activated),
|
||||
[touch]() { touch(); });
|
||||
}
|
||||
|
||||
return combobox;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateLineEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch, bool managed) {
|
||||
const QString text = QString::fromStdString(setting.ToString());
|
||||
line_edit = new QLineEdit(this);
|
||||
line_edit->setText(text);
|
||||
|
||||
serializer = [this]() { return line_edit->text().toStdString(); };
|
||||
|
||||
if (!managed) {
|
||||
return line_edit;
|
||||
}
|
||||
|
||||
restore_func = [this]() {
|
||||
line_edit->setText(QString::fromStdString(RelevantDefault(setting)));
|
||||
};
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); });
|
||||
}
|
||||
|
||||
return line_edit;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateSlider(bool reversed, float multiplier, const QString& given_suffix,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
if (!setting.Ranged()) {
|
||||
LOG_ERROR(Frontend, "\"{}\" is not a ranged setting, but a slider was requested.",
|
||||
setting.GetLabel());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QWidget* container = new QWidget(this);
|
||||
QHBoxLayout* layout = new QHBoxLayout(container);
|
||||
|
||||
slider = new QSlider(Qt::Horizontal, this);
|
||||
QLabel* feedback = new QLabel(this);
|
||||
|
||||
layout->addWidget(slider);
|
||||
layout->addWidget(feedback);
|
||||
|
||||
container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
int max_val = std::stoi(setting.MaxVal());
|
||||
|
||||
QString suffix =
|
||||
given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
|
||||
|
||||
const QString use_format = QStringLiteral("%1").append(suffix);
|
||||
|
||||
QObject::connect(slider, &QAbstractSlider::valueChanged, [=](int value) {
|
||||
int present = (reversed ? max_val - value : value) * multiplier + 0.5f;
|
||||
feedback->setText(use_format.arg(QVariant::fromValue(present).value<QString>()));
|
||||
});
|
||||
|
||||
slider->setMinimum(std::stoi(setting.MinVal()));
|
||||
slider->setMaximum(max_val);
|
||||
slider->setValue(std::stoi(setting.ToString()));
|
||||
|
||||
slider->setInvertedAppearance(reversed);
|
||||
|
||||
serializer = [this]() { return std::to_string(slider->value()); };
|
||||
restore_func = [this]() { slider->setValue(std::stoi(RelevantDefault(setting))); };
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QObject::connect(slider, &QAbstractSlider::actionTriggered, [touch]() { touch(); });
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateSpinBox(const QString& given_suffix,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
const int min_val =
|
||||
setting.Ranged() ? std::stoi(setting.MinVal()) : std::numeric_limits<int>::min();
|
||||
const int max_val =
|
||||
setting.Ranged() ? std::stoi(setting.MaxVal()) : std::numeric_limits<int>::max();
|
||||
const int default_val = std::stoi(setting.ToString());
|
||||
|
||||
QString suffix =
|
||||
given_suffix == QStringLiteral("") ? DefaultSuffix(this, setting) : given_suffix;
|
||||
|
||||
spinbox = new QSpinBox(this);
|
||||
spinbox->setRange(min_val, max_val);
|
||||
spinbox->setValue(default_val);
|
||||
spinbox->setSuffix(suffix);
|
||||
spinbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
serializer = [this]() { return std::to_string(spinbox->value()); };
|
||||
|
||||
restore_func = [this]() {
|
||||
auto value{std::stol(RelevantDefault(setting))};
|
||||
spinbox->setValue(value);
|
||||
};
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QObject::connect(spinbox, QOverload<int>::of(&QSpinBox::valueChanged), [this, touch]() {
|
||||
if (spinbox->value() != std::stoi(setting.ToStringGlobal())) {
|
||||
touch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return spinbox;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateHexEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
auto* data_component = CreateLineEdit(serializer, restore_func, touch, false);
|
||||
if (data_component == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto to_hex = [=](const std::string& input) {
|
||||
return QString::fromStdString(fmt::format("{:08x}", std::stoul(input)));
|
||||
};
|
||||
|
||||
QRegularExpressionValidator* regex = new QRegularExpressionValidator(
|
||||
QRegularExpression{QStringLiteral("^[0-9a-fA-F]{0,8}$")}, line_edit);
|
||||
|
||||
const QString default_val = to_hex(setting.ToString());
|
||||
|
||||
line_edit->setText(default_val);
|
||||
line_edit->setMaxLength(8);
|
||||
line_edit->setValidator(regex);
|
||||
|
||||
auto hex_to_dec = [this]() -> std::string {
|
||||
return std::to_string(std::stoul(line_edit->text().toStdString(), nullptr, 16));
|
||||
};
|
||||
|
||||
serializer = [hex_to_dec]() { return hex_to_dec(); };
|
||||
|
||||
restore_func = [this, to_hex]() { line_edit->setText(to_hex(RelevantDefault(setting))); };
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
|
||||
QObject::connect(line_edit, &QLineEdit::textChanged, [touch]() { touch(); });
|
||||
}
|
||||
|
||||
return line_edit;
|
||||
}
|
||||
|
||||
QWidget* Widget::CreateDateTimeEdit(bool disabled, bool restrict,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch) {
|
||||
const long long current_time = QDateTime::currentSecsSinceEpoch();
|
||||
const s64 the_time = disabled ? current_time : std::stoll(setting.ToString());
|
||||
const auto default_val = QDateTime::fromSecsSinceEpoch(the_time);
|
||||
|
||||
date_time_edit = new QDateTimeEdit(this);
|
||||
date_time_edit->setDateTime(default_val);
|
||||
date_time_edit->setMinimumDateTime(QDateTime::fromSecsSinceEpoch(0));
|
||||
date_time_edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
|
||||
serializer = [this]() { return std::to_string(date_time_edit->dateTime().toSecsSinceEpoch()); };
|
||||
|
||||
auto get_clear_val = [this, restrict, current_time]() {
|
||||
return QDateTime::fromSecsSinceEpoch([this, restrict, current_time]() {
|
||||
if (restrict && checkbox->checkState() == Qt::Checked) {
|
||||
return std::stoll(RelevantDefault(setting));
|
||||
}
|
||||
return current_time;
|
||||
}());
|
||||
};
|
||||
|
||||
restore_func = [this, get_clear_val]() { date_time_edit->setDateTime(get_clear_val()); };
|
||||
|
||||
if (!Settings::IsConfiguringGlobal()) {
|
||||
QObject::connect(date_time_edit, &QDateTimeEdit::editingFinished,
|
||||
[this, get_clear_val, touch]() {
|
||||
if (date_time_edit->dateTime() != get_clear_val()) {
|
||||
touch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return date_time_edit;
|
||||
}
|
||||
|
||||
void Widget::SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
|
||||
RequestType request, float multiplier,
|
||||
Settings::BasicSetting* other_setting, const QString& suffix) {
|
||||
created = true;
|
||||
const auto type = setting.TypeId();
|
||||
|
||||
QLayout* layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
if (other_setting == nullptr) {
|
||||
other_setting = setting.PairedSetting();
|
||||
}
|
||||
|
||||
const bool require_checkbox =
|
||||
other_setting != nullptr && other_setting->TypeId() == typeid(bool);
|
||||
|
||||
if (other_setting != nullptr && other_setting->TypeId() != typeid(bool)) {
|
||||
LOG_WARNING(
|
||||
Frontend,
|
||||
"Extra setting \"{}\" specified but is not bool, refusing to create checkbox for it.",
|
||||
other_setting->GetLabel());
|
||||
}
|
||||
|
||||
std::function<std::string()> checkbox_serializer = []() -> std::string { return {}; };
|
||||
std::function<void()> checkbox_restore_func = []() {};
|
||||
|
||||
std::function<void()> touch = []() {};
|
||||
std::function<std::string()> serializer = []() -> std::string { return {}; };
|
||||
std::function<void()> restore_func = []() {};
|
||||
|
||||
QWidget* data_component{nullptr};
|
||||
|
||||
request = [&]() {
|
||||
if (request != RequestType::Default) {
|
||||
return request;
|
||||
}
|
||||
switch (setting.Specialization() & Settings::SpecializationTypeMask) {
|
||||
case Settings::Specialization::Default:
|
||||
return RequestType::Default;
|
||||
case Settings::Specialization::Time:
|
||||
return RequestType::DateTimeEdit;
|
||||
case Settings::Specialization::Hex:
|
||||
return RequestType::HexEdit;
|
||||
case Settings::Specialization::RuntimeList:
|
||||
managed = false;
|
||||
[[fallthrough]];
|
||||
case Settings::Specialization::List:
|
||||
return RequestType::ComboBox;
|
||||
case Settings::Specialization::Scalar:
|
||||
return RequestType::Slider;
|
||||
case Settings::Specialization::Countable:
|
||||
return RequestType::SpinBox;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return request;
|
||||
}();
|
||||
|
||||
if (!Settings::IsConfiguringGlobal() && managed) {
|
||||
restore_button = CreateRestoreGlobalButton(setting.UsingGlobal(), this);
|
||||
|
||||
touch = [this]() {
|
||||
LOG_DEBUG(Frontend, "Enabling custom setting for \"{}\"", setting.GetLabel());
|
||||
restore_button->setEnabled(true);
|
||||
restore_button->setVisible(true);
|
||||
};
|
||||
}
|
||||
|
||||
if (require_checkbox) {
|
||||
QWidget* lhs =
|
||||
CreateCheckBox(other_setting, label, checkbox_serializer, checkbox_restore_func, touch);
|
||||
layout->addWidget(lhs);
|
||||
} else if (setting.TypeId() != typeid(bool)) {
|
||||
QLabel* qt_label = CreateLabel(label);
|
||||
layout->addWidget(qt_label);
|
||||
}
|
||||
|
||||
if (setting.TypeId() == typeid(bool)) {
|
||||
data_component = CreateCheckBox(&setting, label, serializer, restore_func, touch);
|
||||
} else if (setting.IsEnum()) {
|
||||
data_component = CreateCombobox(serializer, restore_func, touch);
|
||||
} else if (type == typeid(u32) || type == typeid(int) || type == typeid(u16) ||
|
||||
type == typeid(s64) || type == typeid(u8)) {
|
||||
switch (request) {
|
||||
case RequestType::Slider:
|
||||
case RequestType::ReverseSlider:
|
||||
data_component = CreateSlider(request == RequestType::ReverseSlider, multiplier, suffix,
|
||||
serializer, restore_func, touch);
|
||||
break;
|
||||
case RequestType::Default:
|
||||
case RequestType::LineEdit:
|
||||
data_component = CreateLineEdit(serializer, restore_func, touch);
|
||||
break;
|
||||
case RequestType::DateTimeEdit:
|
||||
data_component = CreateDateTimeEdit(other_setting->ToString() != "true", true,
|
||||
serializer, restore_func, touch);
|
||||
break;
|
||||
case RequestType::SpinBox:
|
||||
data_component = CreateSpinBox(suffix, serializer, restore_func, touch);
|
||||
break;
|
||||
case RequestType::HexEdit:
|
||||
data_component = CreateHexEdit(serializer, restore_func, touch);
|
||||
break;
|
||||
case RequestType::ComboBox:
|
||||
data_component = CreateCombobox(serializer, restore_func, touch);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
} else if (type == typeid(std::string)) {
|
||||
switch (request) {
|
||||
case RequestType::Default:
|
||||
case RequestType::LineEdit:
|
||||
data_component = CreateLineEdit(serializer, restore_func, touch);
|
||||
break;
|
||||
case RequestType::ComboBox:
|
||||
data_component = CreateCombobox(serializer, restore_func, touch);
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
|
||||
if (data_component == nullptr) {
|
||||
LOG_ERROR(Frontend, "Failed to create widget for \"{}\"", setting.GetLabel());
|
||||
created = false;
|
||||
return;
|
||||
}
|
||||
|
||||
layout->addWidget(data_component);
|
||||
|
||||
if (!managed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
load_func = [this, serializer, checkbox_serializer, require_checkbox, other_setting]() {
|
||||
if (require_checkbox && other_setting->UsingGlobal()) {
|
||||
other_setting->LoadString(checkbox_serializer());
|
||||
}
|
||||
if (setting.UsingGlobal()) {
|
||||
setting.LoadString(serializer());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
layout->addWidget(restore_button);
|
||||
|
||||
QObject::connect(restore_button, &QAbstractButton::clicked,
|
||||
[this, restore_func, checkbox_restore_func](bool) {
|
||||
LOG_DEBUG(Frontend, "Restore global state for \"{}\"",
|
||||
setting.GetLabel());
|
||||
|
||||
restore_button->setEnabled(false);
|
||||
restore_button->setVisible(false);
|
||||
|
||||
checkbox_restore_func();
|
||||
restore_func();
|
||||
});
|
||||
|
||||
load_func = [this, serializer, require_checkbox, checkbox_serializer, other_setting]() {
|
||||
bool using_global = !restore_button->isEnabled();
|
||||
setting.SetGlobal(using_global);
|
||||
if (!using_global) {
|
||||
setting.LoadString(serializer());
|
||||
}
|
||||
if (require_checkbox) {
|
||||
other_setting->SetGlobal(using_global);
|
||||
if (!using_global) {
|
||||
other_setting->LoadString(checkbox_serializer());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (other_setting != nullptr) {
|
||||
const auto reset = [restore_func, data_component](int state) {
|
||||
data_component->setEnabled(state == Qt::Checked);
|
||||
if (state != Qt::Checked) {
|
||||
restore_func();
|
||||
}
|
||||
};
|
||||
connect(checkbox, &QCheckBox::stateChanged, reset);
|
||||
reset(checkbox->checkState());
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::Valid() const {
|
||||
return created;
|
||||
}
|
||||
|
||||
Widget::~Widget() = default;
|
||||
|
||||
Widget::Widget(Settings::BasicSetting* setting_, const TranslationMap& translations_,
|
||||
const ComboboxTranslationMap& combobox_translations_, QWidget* parent_,
|
||||
bool runtime_lock_, std::vector<std::function<void(bool)>>& apply_funcs_,
|
||||
RequestType request, bool managed, float multiplier,
|
||||
Settings::BasicSetting* other_setting, const QString& suffix)
|
||||
: QWidget(parent_), parent{parent_}, translations{translations_},
|
||||
combobox_enumerations{combobox_translations_}, setting{*setting_}, apply_funcs{apply_funcs_},
|
||||
runtime_lock{runtime_lock_} {
|
||||
if (!Settings::IsConfiguringGlobal() && !setting.Switchable()) {
|
||||
LOG_DEBUG(Frontend, "\"{}\" is not switchable, skipping...", setting.GetLabel());
|
||||
return;
|
||||
}
|
||||
|
||||
const int id = setting.Id();
|
||||
|
||||
const auto [label, tooltip] = [&]() {
|
||||
const auto& setting_label = setting.GetLabel();
|
||||
if (translations.contains(id)) {
|
||||
return std::pair{translations.at(id).first, translations.at(id).second};
|
||||
}
|
||||
LOG_WARNING(Frontend, "Translation table lacks entry for \"{}\"", setting_label);
|
||||
return std::pair{QString::fromStdString(setting_label), QStringLiteral("")};
|
||||
}();
|
||||
|
||||
if (label == QStringLiteral("")) {
|
||||
LOG_DEBUG(Frontend, "Translation table has empty entry for \"{}\", skipping...",
|
||||
setting.GetLabel());
|
||||
return;
|
||||
}
|
||||
|
||||
std::function<void()> load_func = []() {};
|
||||
|
||||
SetupComponent(label, load_func, managed, request, multiplier, other_setting, suffix);
|
||||
|
||||
if (!created) {
|
||||
LOG_WARNING(Frontend, "No widget was created for \"{}\"", setting.GetLabel());
|
||||
return;
|
||||
}
|
||||
|
||||
apply_funcs.push_back([load_func, setting_](bool powered_on) {
|
||||
if (setting_->RuntimeModfiable() || !powered_on) {
|
||||
load_func();
|
||||
}
|
||||
});
|
||||
|
||||
bool enable = runtime_lock || setting.RuntimeModfiable();
|
||||
if (setting.Switchable() && Settings::IsConfiguringGlobal() && !runtime_lock) {
|
||||
enable &= setting.UsingGlobal();
|
||||
}
|
||||
this->setEnabled(enable);
|
||||
|
||||
this->setToolTip(tooltip);
|
||||
}
|
||||
|
||||
Builder::Builder(QWidget* parent_, bool runtime_lock_)
|
||||
: translations{InitializeTranslations(parent_)},
|
||||
combobox_translations{ComboboxEnumeration(parent_)}, parent{parent_}, runtime_lock{
|
||||
runtime_lock_} {}
|
||||
|
||||
Builder::~Builder() = default;
|
||||
|
||||
Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
|
||||
std::vector<std::function<void(bool)>>& apply_funcs,
|
||||
RequestType request, bool managed, float multiplier,
|
||||
Settings::BasicSetting* other_setting, const QString& suffix) const {
|
||||
if (!Settings::IsConfiguringGlobal() && !setting->Switchable()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (setting->Specialization() == Settings::Specialization::Paired) {
|
||||
LOG_DEBUG(Frontend, "\"{}\" has specialization Paired: ignoring", setting->GetLabel());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Widget(setting, *translations, *combobox_translations, parent, runtime_lock,
|
||||
apply_funcs, request, managed, multiplier, other_setting, suffix);
|
||||
}
|
||||
|
||||
Widget* Builder::BuildWidget(Settings::BasicSetting* setting,
|
||||
std::vector<std::function<void(bool)>>& apply_funcs,
|
||||
Settings::BasicSetting* other_setting, RequestType request,
|
||||
const QString& suffix) const {
|
||||
return BuildWidget(setting, apply_funcs, request, true, 1.0f, other_setting, suffix);
|
||||
}
|
||||
|
||||
const ComboboxTranslationMap& Builder::ComboboxTranslations() const {
|
||||
return *combobox_translations;
|
||||
}
|
||||
|
||||
} // namespace ConfigurationShared
|
@ -0,0 +1,161 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <QStringLiteral>
|
||||
#include <QWidget>
|
||||
#include <qobjectdefs.h>
|
||||
#include "yuzu/configuration/shared_translation.h"
|
||||
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDateTimeEdit;
|
||||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QObject;
|
||||
class QPushButton;
|
||||
class QSlider;
|
||||
class QSpinBox;
|
||||
|
||||
namespace Settings {
|
||||
class BasicSetting;
|
||||
} // namespace Settings
|
||||
|
||||
namespace ConfigurationShared {
|
||||
|
||||
enum class RequestType {
|
||||
Default,
|
||||
ComboBox,
|
||||
SpinBox,
|
||||
Slider,
|
||||
ReverseSlider,
|
||||
LineEdit,
|
||||
HexEdit,
|
||||
DateTimeEdit,
|
||||
MaxEnum,
|
||||
};
|
||||
|
||||
class Widget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @param setting The primary Setting to create the Widget for
|
||||
* @param translations Map of translations to display on the left side label/checkbox
|
||||
* @param combobox_translations Map of translations for enumerating combo boxes
|
||||
* @param parent Qt parent
|
||||
* @param runtime_lock Emulated guest powered on state, for use on settings that should be
|
||||
* configured during guest execution
|
||||
* @param apply_funcs_ List to append, functions to run to apply the widget state to the setting
|
||||
* @param request What type of data representation component to create -- not always respected
|
||||
* for the Setting data type
|
||||
* @param managed Set true if the caller will set up component data and handling
|
||||
* @param multiplier Value to multiply the slider feedback label
|
||||
* @param other_setting Second setting to modify, to replace the label with a checkbox
|
||||
* @param suffix Set to specify formats for Slider feedback labels or SpinBox
|
||||
*/
|
||||
explicit Widget(Settings::BasicSetting* setting, const TranslationMap& translations,
|
||||
const ComboboxTranslationMap& combobox_translations, QWidget* parent,
|
||||
bool runtime_lock, std::vector<std::function<void(bool)>>& apply_funcs_,
|
||||
RequestType request = RequestType::Default, bool managed = true,
|
||||
float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
|
||||
const QString& suffix = QStringLiteral(""));
|
||||
virtual ~Widget();
|
||||
|
||||
/**
|
||||
* @returns True if the Widget successfully created the components for the setting
|
||||
*/
|
||||
bool Valid() const;
|
||||
|
||||
/**
|
||||
* Creates a button to appear when a setting has been modified. This exists for custom
|
||||
* configurations and wasn't designed to work for the global configuration. It has public access
|
||||
* for settings that need to be unmanaged but can be custom.
|
||||
*
|
||||
* @param using_global The global state of the setting this button is for
|
||||
* @param parent QWidget parent
|
||||
*/
|
||||
[[nodiscard]] static QPushButton* CreateRestoreGlobalButton(bool using_global, QWidget* parent);
|
||||
|
||||
// Direct handles to sub components created
|
||||
QPushButton* restore_button{}; ///< Restore button for custom configurations
|
||||
QLineEdit* line_edit{}; ///< QLineEdit, used for LineEdit and HexEdit
|
||||
QSpinBox* spinbox{};
|
||||
QCheckBox* checkbox{};
|
||||
QSlider* slider{};
|
||||
QComboBox* combobox{};
|
||||
QDateTimeEdit* date_time_edit{};
|
||||
|
||||
private:
|
||||
void SetupComponent(const QString& label, std::function<void()>& load_func, bool managed,
|
||||
RequestType request, float multiplier,
|
||||
Settings::BasicSetting* other_setting, const QString& suffix);
|
||||
|
||||
QLabel* CreateLabel(const QString& text);
|
||||
QWidget* CreateCheckBox(Settings::BasicSetting* bool_setting, const QString& label,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
|
||||
QWidget* CreateCombobox(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
QWidget* CreateLineEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func, const std::function<void()>& touch,
|
||||
bool managed = true);
|
||||
QWidget* CreateHexEdit(std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func, const std::function<void()>& touch);
|
||||
QWidget* CreateSlider(bool reversed, float multiplier, const QString& suffix,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func, const std::function<void()>& touch);
|
||||
QWidget* CreateDateTimeEdit(bool disabled, bool restrict,
|
||||
std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func,
|
||||
const std::function<void()>& touch);
|
||||
QWidget* CreateSpinBox(const QString& suffix, std::function<std::string()>& serializer,
|
||||
std::function<void()>& restore_func, const std::function<void()>& touch);
|
||||
|
||||
QWidget* parent;
|
||||
const TranslationMap& translations;
|
||||
const ComboboxTranslationMap& combobox_enumerations;
|
||||
Settings::BasicSetting& setting;
|
||||
std::vector<std::function<void(bool)>>& apply_funcs;
|
||||
|
||||
bool created{false};
|
||||
bool runtime_lock{false};
|
||||
};
|
||||
|
||||
class Builder {
|
||||
public:
|
||||
explicit Builder(QWidget* parent, bool runtime_lock);
|
||||
~Builder();
|
||||
|
||||
Widget* BuildWidget(Settings::BasicSetting* setting,
|
||||
std::vector<std::function<void(bool)>>& apply_funcs,
|
||||
RequestType request = RequestType::Default, bool managed = true,
|
||||
float multiplier = 1.0f, Settings::BasicSetting* other_setting = nullptr,
|
||||
const QString& suffix = QStringLiteral("")) const;
|
||||
|
||||
Widget* BuildWidget(Settings::BasicSetting* setting,
|
||||
std::vector<std::function<void(bool)>>& apply_funcs,
|
||||
Settings::BasicSetting* other_setting,
|
||||
RequestType request = RequestType::Default,
|
||||
const QString& suffix = QStringLiteral("")) const;
|
||||
|
||||
const ComboboxTranslationMap& ComboboxTranslations() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<TranslationMap> translations;
|
||||
std::unique_ptr<ComboboxTranslationMap> combobox_translations;
|
||||
|
||||
QWidget* parent;
|
||||
const bool runtime_lock;
|
||||
};
|
||||
|
||||
} // namespace ConfigurationShared
|
Loading…
Reference in New Issue