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-FileCopyrightText: 2016 Citra Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <memory>
|
||||||
#include <QObject>
|
#include <type_traits>
|
||||||
#include <QString>
|
#include <vector>
|
||||||
#include "common/settings.h"
|
|
||||||
#include "yuzu/configuration/configuration_shared.h"
|
#include "yuzu/configuration/configuration_shared.h"
|
||||||
#include "yuzu/configuration/configure_per_game.h"
|
|
||||||
|
|
||||||
void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting,
|
namespace ConfigurationShared {
|
||||||
const QCheckBox* checkbox,
|
|
||||||
const CheckState& tracker) {
|
Tab::Tab(std::shared_ptr<std::vector<Tab*>> group, QWidget* parent) : QWidget(parent) {
|
||||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
if (group != nullptr) {
|
||||||
setting->SetValue(checkbox->checkState());
|
group->push_back(this);
|
||||||
} else if (!Settings::IsConfiguringGlobal()) {
|
|
||||||
if (tracker == CheckState::Global) {
|
|
||||||
setting->SetGlobal(true);
|
|
||||||
} else {
|
|
||||||
setting->SetGlobal(false);
|
|
||||||
setting->SetValue(checkbox->checkState());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox,
|
Tab::~Tab() = default;
|
||||||
const Settings::SwitchableSetting<bool>* setting) {
|
|
||||||
if (setting->UsingGlobal()) {
|
|
||||||
checkbox->setCheckState(Qt::PartiallyChecked);
|
|
||||||
} else {
|
|
||||||
checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) {
|
} // namespace ConfigurationShared
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
@ -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