mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-12-23 11:31:57 +01:00
Move config registry/option to librecomp + added Color conf opt type
This commit is contained in:
parent
4a5e110357
commit
65e3b3eef8
@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 20)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
# set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything /W4")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything /W4")
|
||||||
@ -158,14 +159,14 @@ set (SOURCES
|
|||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_color_hack.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_rml_hacks.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/ui_elements.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/util/hsv.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementConfigGroup.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementConfigGroup.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementConfigOption.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementConfigOption.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeCheckbox.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeCheckbox.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeColor.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRadioTabs.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRadioTabs.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRange.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementOptionTypeRange.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementDescription.cpp
|
${CMAKE_SOURCE_DIR}/src/ui/elements/ElementDescription.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/config_options/ConfigRegistry.cpp
|
|
||||||
${CMAKE_SOURCE_DIR}/src/ui/config_options/ConfigOption.cpp
|
|
||||||
|
|
||||||
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
|
${CMAKE_SOURCE_DIR}/rsp/aspMain.cpp
|
||||||
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
|
${CMAKE_SOURCE_DIR}/rsp/njpgdspMain.cpp
|
||||||
@ -338,4 +339,3 @@ build_pixel_shader(Zelda64Recompiled "shaders/InterfacePS.hlsl" "shaders/Interfa
|
|||||||
target_sources(Zelda64Recompiled PRIVATE ${SOURCES})
|
target_sources(Zelda64Recompiled PRIVATE ${SOURCES})
|
||||||
|
|
||||||
set_property(TARGET Zelda64Recompiled PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
set_property(TARGET Zelda64Recompiled PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
#include "RmlUi/Core.h"
|
#include "RmlUi/Core.h"
|
||||||
|
|
||||||
|
#include "../src/ui/util/hsv.h"
|
||||||
|
|
||||||
namespace Rml {
|
namespace Rml {
|
||||||
class ElementDocument;
|
class ElementDocument;
|
||||||
class EventListenerInstancer;
|
class EventListenerInstancer;
|
||||||
|
@ -44,3 +44,4 @@ recomp_get_inverted_axes = 0x8F0000A4;
|
|||||||
recomp_high_precision_fb_enabled = 0x8F0000A8;
|
recomp_high_precision_fb_enabled = 0x8F0000A8;
|
||||||
recomp_get_resolution_scale = 0x8F0000AC;
|
recomp_get_resolution_scale = 0x8F0000AC;
|
||||||
recomp_get_analog_inverted_axes = 0x8F0000B0;
|
recomp_get_analog_inverted_axes = 0x8F0000B0;
|
||||||
|
recomp_get_config_store_int = 0x8F0000B4;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "librecomp/recomp.h"
|
#include "librecomp/recomp.h"
|
||||||
#include "librecomp/overlays.hpp"
|
#include "librecomp/overlays.hpp"
|
||||||
#include "librecomp/config_store.hpp"
|
#include "librecomp/config.hpp"
|
||||||
#include "zelda_config.h"
|
#include "zelda_config.h"
|
||||||
#include "recomp_input.h"
|
#include "recomp_input.h"
|
||||||
#include "recomp_ui.h"
|
#include "recomp_ui.h"
|
||||||
@ -180,7 +180,7 @@ extern "C" void recomp_get_config_store_int(uint8_t* rdram, recomp_context* ctx)
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_return(ctx, recomp::get_config_store_value<int>(
|
_return(ctx, recomp::config::get_config_store_value<int>(
|
||||||
std::string_view{key_buffer.data(), key_buffer.size()}
|
std::string_view{key_buffer.data(), key_buffer.size()}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
#include "ConfigOption.h"
|
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
namespace recompui {
|
|
||||||
|
|
||||||
bool ConfigOption::validate(json& opt_json) {
|
|
||||||
auto type_struct = get_type_structure();
|
|
||||||
for (const auto& [key, expected_type] : type_struct) {
|
|
||||||
if (!opt_json.contains(key) || opt_json[key].type() != expected_type) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigOption::ConfigOption(json& opt_json)
|
|
||||||
{
|
|
||||||
type = opt_json[ConfigOption::schema::type];
|
|
||||||
key = opt_json[ConfigOption::schema::key];
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigOption::~ConfigOption()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Rml
|
|
@ -1,65 +0,0 @@
|
|||||||
#ifndef RECOMPUI_CONFIG_OPTION_H
|
|
||||||
#define RECOMPUI_CONFIG_OPTION_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include "json/json.hpp"
|
|
||||||
|
|
||||||
namespace recompui {
|
|
||||||
|
|
||||||
enum class ConfigOptionType {
|
|
||||||
Label,
|
|
||||||
// Base types
|
|
||||||
Checkbox,
|
|
||||||
RadioTabs,
|
|
||||||
Dropdown,
|
|
||||||
Range,
|
|
||||||
Trigger,
|
|
||||||
// Group types
|
|
||||||
CheckboxGroup,
|
|
||||||
Group,
|
|
||||||
OptionCount
|
|
||||||
};
|
|
||||||
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(ConfigOptionType, {
|
|
||||||
{ConfigOptionType::Label, "Label"},
|
|
||||||
{ConfigOptionType::Checkbox, "Checkbox"},
|
|
||||||
{ConfigOptionType::RadioTabs, "RadioTabs"},
|
|
||||||
{ConfigOptionType::Dropdown, "Dropdown"},
|
|
||||||
{ConfigOptionType::Range, "Range"},
|
|
||||||
{ConfigOptionType::Trigger, "Trigger"},
|
|
||||||
{ConfigOptionType::CheckboxGroup, "CheckboxGroup"},
|
|
||||||
{ConfigOptionType::Group, "Group"}
|
|
||||||
});
|
|
||||||
|
|
||||||
typedef std::unordered_map<std::string, nlohmann::detail::value_t> config_type_structure;
|
|
||||||
|
|
||||||
inline bool config_option_is_group(ConfigOptionType option_type) {
|
|
||||||
return option_type == ConfigOptionType::Group || option_type == ConfigOptionType::CheckboxGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Base Config Option class. Defines what type of option or group it is, and
|
|
||||||
sets the key.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ConfigOption {
|
|
||||||
public:
|
|
||||||
ConfigOption(nlohmann::json& opt_json);
|
|
||||||
virtual ~ConfigOption();
|
|
||||||
|
|
||||||
bool validate(nlohmann::json& opt_json);
|
|
||||||
|
|
||||||
ConfigOptionType type;
|
|
||||||
std::string key;
|
|
||||||
|
|
||||||
struct schema {
|
|
||||||
static constexpr const char* type = "type";
|
|
||||||
static constexpr const char* key = "key";
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual const config_type_structure& get_type_structure() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,230 +0,0 @@
|
|||||||
#include "ConfigRegistry.h"
|
|
||||||
#include "ConfigOption.h"
|
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
namespace recompui {
|
|
||||||
|
|
||||||
ConfigRegistry config_registry = {{}, {}};
|
|
||||||
|
|
||||||
#define TODO_PARSE_ERROR(m, t) printf("Value at key: \"%s\" was invalid. Expected: \"%s\"\n", m, t)
|
|
||||||
|
|
||||||
std::string get_string_in_json(const json& j, const std::string& key) {
|
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
auto find_it = j.find(key);
|
|
||||||
if (find_it != j.end()) {
|
|
||||||
auto at_val = j.at(key);
|
|
||||||
if (!at_val.is_string()) {
|
|
||||||
TODO_PARSE_ERROR(key, "string");
|
|
||||||
}
|
|
||||||
|
|
||||||
find_it->get_to(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_string_in_json_with_default(const json& j, const std::string& key, const std::string& default_val) {
|
|
||||||
std::string ret;
|
|
||||||
|
|
||||||
auto find_it = j.find(key);
|
|
||||||
if (find_it != j.end()) {
|
|
||||||
auto at_val = j.at(key);
|
|
||||||
if (!at_val.is_string()) {
|
|
||||||
return default_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
find_it->get_to(ret);
|
|
||||||
} else {
|
|
||||||
return default_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool validate_json_value_is_array(const json &j, const std::string& json_path, const std::string& arr_key) {
|
|
||||||
const auto &options = j.find(arr_key);
|
|
||||||
if (options == j.end()) {
|
|
||||||
TODO_PARSE_ERROR(json_path + "/" + arr_key, "array");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto &opt_array = j[arr_key];
|
|
||||||
|
|
||||||
if (!opt_array.is_array()) {
|
|
||||||
TODO_PARSE_ERROR(json_path + "/" + arr_key, "array");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Option
|
|
||||||
void register_config_option(
|
|
||||||
const json& j,
|
|
||||||
const std::string& previous_key, // previous path before current key
|
|
||||||
const std::string& config_group,
|
|
||||||
const std::string& json_path // path to this json object from the root
|
|
||||||
) {
|
|
||||||
const std::string key = get_string_in_json(j, ConfigOption::schema::key);
|
|
||||||
const std::string this_key = previous_key + "/" + key;
|
|
||||||
|
|
||||||
ConfigOptionType type = get_value_in_json<ConfigOptionType>(j, ConfigOption::schema::type);
|
|
||||||
|
|
||||||
config_registry.key_ref_map[this_key] = { config_group, json_path };
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case ConfigOptionType::Checkbox: {
|
|
||||||
bool default_val = false;
|
|
||||||
if (j.find("default") != j.end()) {
|
|
||||||
default_val = get_value_in_json<bool>(j, "default");
|
|
||||||
}
|
|
||||||
recomp::set_config_store_value_and_default(this_key, default_val, default_val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ConfigOptionType::RadioTabs: {
|
|
||||||
if (!validate_json_value_is_array(j, json_path, "values")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int default_val = 0;
|
|
||||||
if (j.find("default") != j.end()) {
|
|
||||||
const auto &opt_array = j["values"];
|
|
||||||
const std::string default_val_string = get_string_in_json(j, "default");
|
|
||||||
// Based on default value's string, find which option index corresponds
|
|
||||||
for (int i = 0; i < opt_array.size(); i++) {
|
|
||||||
const auto &j_opt = opt_array[i];
|
|
||||||
if (!j_opt.is_string()) {
|
|
||||||
TODO_PARSE_ERROR(key + "/values/" + std::to_string(i) , "string");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const std::string opt_val = j_opt.get<std::string>();
|
|
||||||
if (opt_val == default_val_string) {
|
|
||||||
default_val = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recomp::set_config_store_value_and_default(this_key, default_val, default_val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ConfigOptionType::Range: {
|
|
||||||
int default_val = 0;
|
|
||||||
int max = 0;
|
|
||||||
int min = 0;
|
|
||||||
int step = 1;
|
|
||||||
|
|
||||||
if (j.find("default") != j.end()) {
|
|
||||||
default_val = get_value_in_json<int>(j, "default");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max is required
|
|
||||||
if (j.find("max") != j.end()) {
|
|
||||||
max = get_value_in_json<int>(j, "max");
|
|
||||||
if (default_val > max) default_val = max;
|
|
||||||
} else {
|
|
||||||
TODO_PARSE_ERROR(key + "/max", "int");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.find("min") != j.end()) {
|
|
||||||
min = get_value_in_json<int>(j, "min");
|
|
||||||
if (default_val < min) default_val = min;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.find("step") != j.end()) {
|
|
||||||
step = get_value_in_json<int>(j, "step");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(max > min);
|
|
||||||
assert(step < max - min);
|
|
||||||
recomp::set_config_store_value_and_default(this_key, default_val, default_val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (j.find("label") != j.end()) {
|
|
||||||
const std::string label = get_string_in_json(j, "label");
|
|
||||||
recomp::set_config_store_value(this_key, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((type == ConfigOptionType::Group || type == ConfigOptionType::CheckboxGroup) && j.find("options") != j.end()) {
|
|
||||||
if (!validate_json_value_is_array(j, json_path, "options")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &opt_array = j["options"];
|
|
||||||
|
|
||||||
for (int i = 0; i < opt_array.size(); i++) {
|
|
||||||
const auto &el = opt_array[i];
|
|
||||||
register_config_option(
|
|
||||||
el,
|
|
||||||
this_key,
|
|
||||||
config_group,
|
|
||||||
json_path + "/options/" + std::to_string(i)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_config(const std::string &json_str, const std::string &config_group) {
|
|
||||||
config_registry.group_json_map[config_group] = json::parse(json_str);
|
|
||||||
const auto &j = config_registry.group_json_map[config_group];
|
|
||||||
|
|
||||||
if (!j.is_array()) {
|
|
||||||
TODO_PARSE_ERROR("/", "array");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < j.size(); i++) {
|
|
||||||
const auto &el = j[i];
|
|
||||||
register_config_option(
|
|
||||||
el,
|
|
||||||
config_group,
|
|
||||||
config_group,
|
|
||||||
"/" + std::to_string(i) // json_path at top level
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_translation(const std::string &json_str, const std::string &config_group) {
|
|
||||||
const auto &j = json::parse(json_str);
|
|
||||||
|
|
||||||
if (!j.is_object()) {
|
|
||||||
TODO_PARSE_ERROR("/", "object");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& el : j.items())
|
|
||||||
{
|
|
||||||
std::string translation_key = "translations/" + config_group + "/" + el.key();
|
|
||||||
const std::string value = el.value();
|
|
||||||
recomp::set_config_store_value(translation_key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
json get_json_from_key(const std::string &config_key) {
|
|
||||||
if (config_registry.key_ref_map.find(config_key) == config_registry.key_ref_map.end()) {
|
|
||||||
// TODO: handle not finding config_key
|
|
||||||
printf("FAILURE: AddOptionTypeElement failed to find config_key '%s' in config_registry.key_ref_map\n", config_key);
|
|
||||||
}
|
|
||||||
const JSONRef& json_ref = config_registry.key_ref_map[config_key];
|
|
||||||
const json& group_json = config_registry.group_json_map[json_ref.config_group];
|
|
||||||
json::json_pointer pointer(json_ref.json_path);
|
|
||||||
return group_json[pointer];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool config_key_is_base_group(const std::string &config_group_key) {
|
|
||||||
// determine if the key references a base group by checking the group_json_map
|
|
||||||
if (config_registry.group_json_map.find(config_group_key) == config_registry.group_json_map.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
json& get_group_json(const std::string &config_group_key) {
|
|
||||||
return config_registry.group_json_map[config_group_key];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace recompui
|
|
@ -1,66 +0,0 @@
|
|||||||
#ifndef RECOMPUI_CONFIG_REGISTRY_H
|
|
||||||
#define RECOMPUI_CONFIG_REGISTRY_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <variant>
|
|
||||||
#include <map>
|
|
||||||
#include "json/json.hpp"
|
|
||||||
|
|
||||||
namespace recompui {
|
|
||||||
struct JSONRef {
|
|
||||||
std::string config_group;
|
|
||||||
std::string json_path; // used as a json pointer
|
|
||||||
};
|
|
||||||
// config key -> JSONRef
|
|
||||||
typedef std::unordered_map<std::string, JSONRef> config_registry_key_reference_map;
|
|
||||||
// config group -> json
|
|
||||||
typedef std::unordered_map<std::string, nlohmann::json> config_registry_group_json_map;
|
|
||||||
|
|
||||||
struct ConfigRegistry {
|
|
||||||
config_registry_key_reference_map key_ref_map;
|
|
||||||
config_registry_group_json_map group_json_map;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern ConfigRegistry config_registry;
|
|
||||||
|
|
||||||
void register_config(const std::string& json_str, const std::string& config_group);
|
|
||||||
void register_translation(const std::string &json_str, const std::string &config_group);
|
|
||||||
|
|
||||||
nlohmann::json get_json_from_key(const std::string &config_key);
|
|
||||||
std::string get_string_in_json(const nlohmann::json& j, const std::string& key);
|
|
||||||
std::string get_string_in_json_with_default(const nlohmann::json& j, const std::string& key, const std::string& default_val);
|
|
||||||
bool config_key_is_base_group(const std::string &config_group_key);
|
|
||||||
nlohmann::json& get_group_json(const std::string &config_group_key);
|
|
||||||
|
|
||||||
#define TODO_PARSE_ERROR(m, t) printf("Value at key: \"%s\" was invalid. Expected: \"%s\"\n", m, t)
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T get_value_in_json(const nlohmann::json& j, const std::string& key) {
|
|
||||||
T ret;
|
|
||||||
|
|
||||||
auto find_it = j.find(key);
|
|
||||||
if (find_it != j.end()) {
|
|
||||||
auto at_val = j.at(key);
|
|
||||||
find_it->get_to(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T get_value_in_json_with_default(const nlohmann::json& j, const std::string& key, T default_val) {
|
|
||||||
T ret = default_val;
|
|
||||||
|
|
||||||
auto find_it = j.find(key);
|
|
||||||
if (find_it != j.end()) {
|
|
||||||
auto at_val = j.at(key);
|
|
||||||
find_it->get_to(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,17 +1,12 @@
|
|||||||
|
|
||||||
#include "ElementConfigGroup.h"
|
#include "ElementConfigGroup.h"
|
||||||
#include "ElementConfigOption.h"
|
#include "ElementConfigOption.h"
|
||||||
#include "../config_options/ConfigOption.h"
|
|
||||||
#include "../config_options/ConfigRegistry.h"
|
|
||||||
#include "ElementOptionTypeCheckbox.h"
|
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "recomp_ui.h"
|
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
|
||||||
#include <RmlUi/Core/ElementText.h>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
using ConfigOptionType = recomp::config::ConfigOptionType;
|
||||||
|
using ConfigOption = recomp::config::ConfigOption;
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
@ -62,7 +57,7 @@ void ElementConfigGroup::AddConfigOptionElement(const json& option_json) {
|
|||||||
ConfigOptionType el_option_type = ConfigOptionType::Label;
|
ConfigOptionType el_option_type = ConfigOptionType::Label;
|
||||||
from_json(option_json["type"], el_option_type);
|
from_json(option_json["type"], el_option_type);
|
||||||
|
|
||||||
const std::string key = get_string_in_json(option_json, ConfigOption::schema::key);
|
const std::string key = recomp::config::get_string_in_json(option_json, ConfigOption::schema::key);
|
||||||
|
|
||||||
Rml::Element *option_container = GetChild(1);
|
Rml::Element *option_container = GetChild(1);
|
||||||
Rml::Element *child_opt = nullptr;
|
Rml::Element *child_opt = nullptr;
|
||||||
@ -87,11 +82,11 @@ void ElementConfigGroup::AddConfigOptionElement(const json& option_json) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static nlohmann::json get_options(std::string& config_key) {
|
static nlohmann::json get_options(std::string& config_key) {
|
||||||
if (config_key_is_base_group(config_key)) {
|
if (recomp::config::config_key_is_base_group(config_key)) {
|
||||||
return get_group_json(config_key);
|
return recomp::config::get_group_json(config_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
const json& group_json = get_json_from_key(config_key);
|
const json& group_json = recomp::config::get_json_from_key(config_key);
|
||||||
return group_json["options"];
|
return group_json["options"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +98,7 @@ void ElementConfigGroup::OnAttributeChange(const Rml::ElementAttributes& changed
|
|||||||
auto config_store_key_attr = changed_attributes.find("recomp-data");
|
auto config_store_key_attr = changed_attributes.find("recomp-data");
|
||||||
if (config_store_key_attr != changed_attributes.end() && config_store_key_attr->second.GetType() == Rml::Variant::STRING) {
|
if (config_store_key_attr != changed_attributes.end() && config_store_key_attr->second.GetType() == Rml::Variant::STRING) {
|
||||||
config_key = config_store_key_attr->second.Get<Rml::String>();
|
config_key = config_store_key_attr->second.Get<Rml::String>();
|
||||||
bool is_base_group = config_key_is_base_group(config_key);
|
bool is_base_group = recomp::config::config_key_is_base_group(config_key);
|
||||||
|
|
||||||
option_type = ConfigOptionType::Label;
|
option_type = ConfigOptionType::Label;
|
||||||
|
|
||||||
@ -113,13 +108,13 @@ void ElementConfigGroup::OnAttributeChange(const Rml::ElementAttributes& changed
|
|||||||
ToggleTextLabelVisibility(false);
|
ToggleTextLabelVisibility(false);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
auto value = recomp::get_config_store_value<std::string>("translations/" + config_key);
|
auto value = recomp::config::get_config_store_value<std::string>("translations/" + config_key);
|
||||||
SetTextLabel(value);
|
SetTextLabel(value);
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
SetTextLabel(e.what());
|
SetTextLabel(e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
const json& group_json = get_json_from_key(config_key);
|
const json& group_json = recomp::config::get_json_from_key(config_key);
|
||||||
|
|
||||||
from_json(group_json["type"], option_type);
|
from_json(group_json["type"], option_type);
|
||||||
assert(
|
assert(
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#ifndef RECOMPUI_ELEMENTS_CONFIG_GROUP_H
|
#ifndef RECOMPUI_ELEMENTS_CONFIG_GROUP_H
|
||||||
#define RECOMPUI_ELEMENTS_CONFIG_GROUP_H
|
#define RECOMPUI_ELEMENTS_CONFIG_GROUP_H
|
||||||
|
|
||||||
#include "RmlUi/Core/Element.h"
|
#include "common.h"
|
||||||
#include "json/json.hpp"
|
|
||||||
#include "../config_options/ConfigOption.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
@ -17,7 +15,7 @@ public:
|
|||||||
ElementConfigGroup(const Rml::String& tag);
|
ElementConfigGroup(const Rml::String& tag);
|
||||||
virtual ~ElementConfigGroup();
|
virtual ~ElementConfigGroup();
|
||||||
|
|
||||||
ConfigOptionType option_type;
|
recomp::config::ConfigOptionType option_type;
|
||||||
std::string config_key;
|
std::string config_key;
|
||||||
protected:
|
protected:
|
||||||
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
|
|
||||||
#include "ElementConfigOption.h"
|
#include "ElementConfigOption.h"
|
||||||
#include "../config_options/ConfigOption.h"
|
|
||||||
#include "../config_options/ConfigRegistry.h"
|
#include "../ui_elements.h"
|
||||||
#include "ElementOptionTypeCheckbox.h"
|
|
||||||
#include "ElementOptionTypeRadioTabs.h"
|
|
||||||
#include "ElementOptionTypeRange.h"
|
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "recomp_ui.h"
|
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
|
||||||
#include <RmlUi/Core/ElementText.h>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
using ConfigOptionType = recomp::config::ConfigOptionType;
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
@ -50,9 +45,14 @@ ElementConfigOption::~ElementConfigOption()
|
|||||||
RemoveEventListener(Rml::EventId::Mouseover, this, true);
|
RemoveEventListener(Rml::EventId::Mouseover, this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rml::Element *ElementConfigOption::GetLabel() {
|
||||||
|
Rml::ElementText *text_label = (Rml::ElementText *)GetElementById("config-opt-label");
|
||||||
|
return text_label->GetParentNode();
|
||||||
|
}
|
||||||
|
|
||||||
void ElementConfigOption::SetTextLabel(const std::string& s) {
|
void ElementConfigOption::SetTextLabel(const std::string& s) {
|
||||||
Rml::ElementText *label = (Rml::ElementText *)GetElementById("config-opt-label");
|
Rml::ElementText *text_label = (Rml::ElementText *)GetElementById("config-opt-label");
|
||||||
label->SetText(s);
|
text_label->SetText(s);
|
||||||
DirtyLayout();
|
DirtyLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +68,8 @@ static void add_option_el(Rml::ElementDocument *doc, Rml::Element *wrapper, cons
|
|||||||
|
|
||||||
void ElementConfigOption::AddOptionTypeElement() {
|
void ElementConfigOption::AddOptionTypeElement() {
|
||||||
ConfigOptionType el_option_type = ConfigOptionType::Label;
|
ConfigOptionType el_option_type = ConfigOptionType::Label;
|
||||||
const json& option_json = get_json_from_key(config_key);
|
const json& option_json = recomp::config::get_json_from_key(config_key);
|
||||||
from_json(option_json["type"], el_option_type);
|
recomp::config::from_json(option_json["type"], el_option_type);
|
||||||
|
|
||||||
Rml::Element *wrapper = GetOptionTypeWrapper();
|
Rml::Element *wrapper = GetOptionTypeWrapper();
|
||||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||||
@ -82,6 +82,10 @@ void ElementConfigOption::AddOptionTypeElement() {
|
|||||||
add_option_el<ElementOptionTypeCheckbox>(doc, wrapper, "recomp-option-type-checkbox", config_key);
|
add_option_el<ElementOptionTypeCheckbox>(doc, wrapper, "recomp-option-type-checkbox", config_key);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ConfigOptionType::Color: {
|
||||||
|
add_option_el<ElementOptionTypeColor>(doc, wrapper, "recomp-option-type-color", config_key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ConfigOptionType::RadioTabs: {
|
case ConfigOptionType::RadioTabs: {
|
||||||
add_option_el<ElementOptionTypeRadioTabs>(doc, wrapper, "recomp-option-type-radio-tabs", config_key);
|
add_option_el<ElementOptionTypeRadioTabs>(doc, wrapper, "recomp-option-type-radio-tabs", config_key);
|
||||||
break;
|
break;
|
||||||
@ -107,8 +111,11 @@ void ElementConfigOption::OnAttributeChange(const Rml::ElementAttributes& change
|
|||||||
SetClass(config_option_base_class_hz, true);
|
SetClass(config_option_base_class_hz, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rml::Element *label = GetLabel();
|
||||||
|
label->SetAttribute("for", "input__" + config_key);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto value = recomp::get_config_store_value<std::string>("translations/" + config_key);
|
auto value = recomp::config::get_config_store_value<std::string>("translations/" + config_key);
|
||||||
SetTextLabel(value);
|
SetTextLabel(value);
|
||||||
printf("found type and translation\n");
|
printf("found type and translation\n");
|
||||||
AddOptionTypeElement();
|
AddOptionTypeElement();
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#ifndef RECOMPUI_ELEMENTS_CONFIG_OPTION_H
|
#ifndef RECOMPUI_ELEMENTS_CONFIG_OPTION_H
|
||||||
#define RECOMPUI_ELEMENTS_CONFIG_OPTION_H
|
#define RECOMPUI_ELEMENTS_CONFIG_OPTION_H
|
||||||
|
|
||||||
#include "RmlUi/Core/Element.h"
|
#include "common.h"
|
||||||
#include "../config_options/ConfigOption.h"
|
|
||||||
#include "RmlUi/Core/EventListener.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
@ -17,12 +15,13 @@ public:
|
|||||||
ElementConfigOption(const Rml::String& tag);
|
ElementConfigOption(const Rml::String& tag);
|
||||||
virtual ~ElementConfigOption();
|
virtual ~ElementConfigOption();
|
||||||
|
|
||||||
ConfigOptionType option_type;
|
recomp::config::ConfigOptionType option_type;
|
||||||
std::string config_key;
|
std::string config_key;
|
||||||
bool in_checkbox_group = false;
|
bool in_checkbox_group = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
||||||
|
Rml::Element *GetLabel(void);
|
||||||
void SetTextLabel(const std::string& s);
|
void SetTextLabel(const std::string& s);
|
||||||
void AddOptionTypeElement();
|
void AddOptionTypeElement();
|
||||||
Rml::Element *GetOptionTypeWrapper();
|
Rml::Element *GetOptionTypeWrapper();
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
|
|
||||||
#include "ElementDescription.h"
|
#include "ElementDescription.h"
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include "../config_options/ConfigRegistry.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
|
||||||
#include <RmlUi/Core/ElementText.h>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@ -50,7 +46,7 @@ void ElementDescription::OnAttributeChange(const Rml::ElementAttributes& changed
|
|||||||
config_key = config_store_key_attr->second.Get<Rml::String>();
|
config_key = config_store_key_attr->second.Get<Rml::String>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto value = recomp::get_config_store_value<std::string>("translations/" + config_key + ":description");
|
auto value = recomp::config::get_config_store_value<std::string>("translations/" + config_key + ":description");
|
||||||
update_text(value);
|
update_text(value);
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
update_text(default_contents);
|
update_text(default_contents);
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
#ifndef RECOMPUI_ELEMENT_DESCRIPTION_H
|
#ifndef RECOMPUI_ELEMENT_DESCRIPTION_H
|
||||||
#define RECOMPUI_ELEMENT_DESCRIPTION_H
|
#define RECOMPUI_ELEMENT_DESCRIPTION_H
|
||||||
|
|
||||||
#include "RmlUi/Core/Element.h"
|
#include "common.h"
|
||||||
#include "../config_options/ConfigOption.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
|
|
||||||
#include "ElementOptionTypeCheckbox.h"
|
#include "ElementOptionTypeCheckbox.h"
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include "../config_options/ConfigRegistry.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
|
||||||
#include <RmlUi/Core/ElementText.h>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@ -51,8 +48,8 @@ void ElementOptionTypeCheckbox::set_checked(bool checked) {
|
|||||||
|
|
||||||
void ElementOptionTypeCheckbox::init_option(std::string& _config_key) {
|
void ElementOptionTypeCheckbox::init_option(std::string& _config_key) {
|
||||||
config_key = _config_key;
|
config_key = _config_key;
|
||||||
const json& option_json = get_json_from_key(config_key);
|
const json& option_json = recomp::config::get_json_from_key(config_key);
|
||||||
int value = recomp::get_config_store_value<int>(config_key);
|
int value = recomp::config::get_config_store_value<int>(config_key);
|
||||||
set_checked(value);
|
set_checked(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,8 +60,8 @@ void ElementOptionTypeCheckbox::ProcessEvent(Rml::Event& event)
|
|||||||
{
|
{
|
||||||
if (event.GetPhase() == Rml::EventPhase::Capture || event.GetPhase() == Rml::EventPhase::Target)
|
if (event.GetPhase() == Rml::EventPhase::Capture || event.GetPhase() == Rml::EventPhase::Target)
|
||||||
{
|
{
|
||||||
bool new_value = !recomp::get_config_store_value<int>(config_key);
|
bool new_value = !recomp::config::get_config_store_value<int>(config_key);
|
||||||
recomp::set_config_store_value(config_key, new_value);
|
recomp::config::set_config_store_value(config_key, new_value);
|
||||||
set_checked(new_value);
|
set_checked(new_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_CHECKBOX_H
|
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_CHECKBOX_H
|
||||||
#define RECOMPUI_ELEMENT_OPTION_TYPE_CHECKBOX_H
|
#define RECOMPUI_ELEMENT_OPTION_TYPE_CHECKBOX_H
|
||||||
|
|
||||||
#include "RmlUi/Core/Element.h"
|
#include "common.h"
|
||||||
#include "RmlUi/Core/Elements/ElementFormControlInput.h"
|
|
||||||
#include "RmlUi/Core/EventListener.h"
|
|
||||||
#include "../config_options/ConfigOption.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
@ -17,7 +14,7 @@ public:
|
|||||||
|
|
||||||
std::string config_key;
|
std::string config_key;
|
||||||
|
|
||||||
const ConfigOptionType option_type = ConfigOptionType::Checkbox;
|
const recomp::config::ConfigOptionType option_type = recomp::config::ConfigOptionType::Checkbox;
|
||||||
protected:
|
protected:
|
||||||
Rml::ElementFormControlInput *get_input();
|
Rml::ElementFormControlInput *get_input();
|
||||||
|
|
||||||
|
185
src/ui/elements/ElementOptionTypeColor.cpp
Normal file
185
src/ui/elements/ElementOptionTypeColor.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#include "ElementOptionTypeColor.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
static const std::string range_input_id = "recomp-color-range:"; // + H/S/V
|
||||||
|
static const std::string range_label_id = "recomp-color-range-label:"; // + H/S/V
|
||||||
|
static const std::string hsv_label[] = {"H", "S", "V"};
|
||||||
|
|
||||||
|
static const std::string preview_block_id = "recomp-color-block";
|
||||||
|
|
||||||
|
static const std::string cls_base = "config-option-color";
|
||||||
|
static const std::string cls_color_preview_wrapper = cls_base + "__preview-wrapper";
|
||||||
|
static const std::string cls_color_preview_block = cls_base + "__preview-block";
|
||||||
|
static const std::string cls_color_hsv_wrapper = cls_base + "__hsv-wrapper";
|
||||||
|
|
||||||
|
// TODO: use these 3 directly from ElementOptionTypeRange
|
||||||
|
static const std::string range_cls_base = "config-option-range";
|
||||||
|
static const std::string range_cls_label = cls_base + "__label";
|
||||||
|
static const std::string range_cls_range_input = cls_base + "__range-input";
|
||||||
|
|
||||||
|
|
||||||
|
ElementOptionTypeColor::ElementOptionTypeColor(const Rml::String& tag) : Rml::Element(tag)
|
||||||
|
{
|
||||||
|
SetAttribute("recomp-store-element", true);
|
||||||
|
SetClass(cls_base, true);
|
||||||
|
|
||||||
|
hsv.h = 0;
|
||||||
|
hsv.s = 0;
|
||||||
|
hsv.v = 0;
|
||||||
|
|
||||||
|
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||||
|
|
||||||
|
{
|
||||||
|
Rml::Element *preview_wrapper = AppendChild(doc->CreateElement("div"));
|
||||||
|
preview_wrapper->SetClass(cls_color_preview_wrapper, true);
|
||||||
|
{
|
||||||
|
Rml::Element *preview_block = preview_wrapper->AppendChild(doc->CreateElement("div"));
|
||||||
|
preview_block->SetClass(cls_color_preview_block, true);
|
||||||
|
preview_block->SetId(preview_block_id);
|
||||||
|
|
||||||
|
Rml::Element *hsv_wrapper = preview_wrapper->AppendChild(doc->CreateElement("div"));
|
||||||
|
hsv_wrapper->SetClass(cls_color_hsv_wrapper, true);
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
const auto &label = hsv_label[i];
|
||||||
|
|
||||||
|
Rml::Element *range_wrapper = hsv_wrapper->AppendChild(doc->CreateElement("div"));
|
||||||
|
range_wrapper->SetClass(range_cls_base, true);
|
||||||
|
{
|
||||||
|
Rml::Element *label_el = range_wrapper->AppendChild(doc->CreateElement("label"));
|
||||||
|
label_el->SetClass(range_cls_label, true);
|
||||||
|
label_el->SetId(range_label_id);
|
||||||
|
{
|
||||||
|
Rml::Element *text_node = label_el->AppendChild(doc->CreateTextNode(""));
|
||||||
|
text_node->SetId(range_label_id + label);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rml::ElementFormControlInput *range_el = (Rml::ElementFormControlInput *)range_wrapper->AppendChild(doc->CreateElement("input"));
|
||||||
|
range_el->SetClass(range_cls_range_input, true);
|
||||||
|
range_el->SetId(range_input_id + label);
|
||||||
|
range_el->SetAttribute("type", "range");
|
||||||
|
range_el->SetAttribute("min", 0);
|
||||||
|
range_el->SetAttribute("hsv-index", i);
|
||||||
|
if (i == 0) {
|
||||||
|
range_el->SetValue(std::to_string((int)round(hsv[i])));
|
||||||
|
range_el->SetAttribute("max", 360);
|
||||||
|
} else {
|
||||||
|
range_el->SetValue(std::to_string((int)round(hsv[i] * 100.0f)));
|
||||||
|
range_el->SetAttribute("max", 100);
|
||||||
|
}
|
||||||
|
range_el->AddEventListener(Rml::EventId::Change, this, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: RGB hex input
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementOptionTypeColor::~ElementOptionTypeColor()
|
||||||
|
{
|
||||||
|
Rml::ElementList elements;
|
||||||
|
GetElementsByTagName(elements, "input");
|
||||||
|
for (int i = 0; i < elements.size(); i++) {
|
||||||
|
Rml::Element *el = elements[i];
|
||||||
|
el->RemoveEventListener(Rml::EventId::Click, this, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementOptionTypeColor::set_value_label(int hsvIndex) {
|
||||||
|
const auto value = hsv[hsvIndex];
|
||||||
|
const auto& which = hsv_label[hsvIndex];
|
||||||
|
|
||||||
|
Rml::ElementText *text_label = (Rml::ElementText *)GetElementById(range_label_id + which);
|
||||||
|
if (hsvIndex == 0) {
|
||||||
|
text_label->SetText(which + ": " + std::to_string((int)round(value)));
|
||||||
|
} else {
|
||||||
|
text_label->SetText(which + ": " + std::to_string((int)round(value * 100.0f)));
|
||||||
|
}
|
||||||
|
DirtyLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementOptionTypeColor::set_preview_block_rgb(RgbColor rgb) {
|
||||||
|
Rml::Element *color_block = GetElementById(preview_block_id);
|
||||||
|
char hex_buf[8]; // Enough to hold "#RRGGBB\0"
|
||||||
|
sprintf(hex_buf, "#%02x%02x%02x", rgb.r, rgb.g, rgb.b);
|
||||||
|
const std::string hex_val = std::string(hex_buf);
|
||||||
|
|
||||||
|
color_block->SetProperty("background-color", hex_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementOptionTypeColor::set_config_store_rgb() {
|
||||||
|
RgbColor rgb;
|
||||||
|
HsvFToRgb(hsv, rgb);
|
||||||
|
recomp::config::set_config_store_value(config_key + ":r", rgb.r);
|
||||||
|
recomp::config::set_config_store_value(config_key + ":g", rgb.g);
|
||||||
|
recomp::config::set_config_store_value(config_key + ":b", rgb.b);
|
||||||
|
set_preview_block_rgb(rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ElementOptionTypeColor::init_option(std::string& _config_key) {
|
||||||
|
|
||||||
|
|
||||||
|
config_key = _config_key;
|
||||||
|
|
||||||
|
const json& option_json = recomp::config::get_json_from_key(config_key);
|
||||||
|
|
||||||
|
RgbColor col;
|
||||||
|
HsvColor hsv_uc;
|
||||||
|
col.r = recomp::config::get_config_store_value<int>(config_key + ":r");
|
||||||
|
col.g = recomp::config::get_config_store_value<int>(config_key + ":g");
|
||||||
|
col.b = recomp::config::get_config_store_value<int>(config_key + ":b");
|
||||||
|
|
||||||
|
RgbToHsv(col, hsv_uc);
|
||||||
|
hsv.h = std::clamp(hsv_uc.h * 360.0f, 0.0f, 360.0f);
|
||||||
|
hsv.s = std::clamp((float)hsv_uc.s / 255.0f, 0.0f, 1.0f);
|
||||||
|
hsv.v = std::clamp((float)hsv_uc.v / 255.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
set_preview_block_rgb(col);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
const auto &label = hsv_label[i];
|
||||||
|
|
||||||
|
Rml::ElementFormControlInput *range = (Rml::ElementFormControlInput *)GetElementById(range_input_id + label);
|
||||||
|
if (i == 0) {
|
||||||
|
range->SetValue(std::to_string((int)round(hsv[i])));
|
||||||
|
} else {
|
||||||
|
range->SetValue(std::to_string((int)round(hsv[i] * 100.0f)));
|
||||||
|
}
|
||||||
|
set_value_label(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ElementOptionTypeColor::ProcessEvent(Rml::Event& event)
|
||||||
|
{
|
||||||
|
if (event == Rml::EventId::Change)
|
||||||
|
{
|
||||||
|
if (event.GetPhase() == Rml::EventPhase::Bubble || event.GetPhase() == Rml::EventPhase::Target)
|
||||||
|
{
|
||||||
|
Rml::Element *target = event.GetTargetElement();
|
||||||
|
auto val_variant = target->GetAttribute("value");
|
||||||
|
int new_value = val_variant->Get<int>();
|
||||||
|
|
||||||
|
auto idx_variant = target->GetAttribute("hsv-index");
|
||||||
|
auto hsv_index = idx_variant->Get<int>();
|
||||||
|
if (hsv_index == 0) {
|
||||||
|
hsv[hsv_index] = (float)new_value;
|
||||||
|
} else {
|
||||||
|
hsv[hsv_index] = (float)new_value / 100.0f;
|
||||||
|
}
|
||||||
|
set_value_label(hsv_index);
|
||||||
|
set_config_store_rgb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Rml
|
26
src/ui/elements/ElementOptionTypeColor.h
Normal file
26
src/ui/elements/ElementOptionTypeColor.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_COLOR_H
|
||||||
|
#define RECOMPUI_ELEMENT_OPTION_TYPE_COLOR_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
class ElementOptionTypeColor : public Rml::Element, public Rml::EventListener {
|
||||||
|
public:
|
||||||
|
ElementOptionTypeColor(const Rml::String& tag);
|
||||||
|
virtual ~ElementOptionTypeColor();
|
||||||
|
|
||||||
|
std::string config_key;
|
||||||
|
HsvColorF hsv;
|
||||||
|
|
||||||
|
void init_option(std::string& _config_key);
|
||||||
|
protected:
|
||||||
|
void set_value_label(int hsvIndex);
|
||||||
|
void set_config_store_rgb();
|
||||||
|
void set_preview_block_rgb(RgbColor rgb);
|
||||||
|
|
||||||
|
void ProcessEvent(Rml::Event& event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace recompui
|
||||||
|
#endif
|
@ -1,10 +1,7 @@
|
|||||||
|
|
||||||
#include "ElementOptionTypeRadioTabs.h"
|
#include "ElementOptionTypeRadioTabs.h"
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include "../config_options/ConfigRegistry.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
|
||||||
#include <RmlUi/Core/ElementText.h>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
@ -47,8 +44,8 @@ void ElementOptionTypeRadioTabs::set_cur_option(int opt) {
|
|||||||
|
|
||||||
void ElementOptionTypeRadioTabs::init_option(std::string& _config_key) {
|
void ElementOptionTypeRadioTabs::init_option(std::string& _config_key) {
|
||||||
config_key = _config_key;
|
config_key = _config_key;
|
||||||
const json& option_json = get_json_from_key(config_key);
|
const json& option_json = recomp::config::get_json_from_key(config_key);
|
||||||
int opt = recomp::get_config_store_value<int>(config_key);
|
int opt = recomp::config::get_config_store_value<int>(config_key);
|
||||||
const json& opt_array = option_json["values"];
|
const json& opt_array = option_json["values"];
|
||||||
|
|
||||||
for (int i = 0; i < opt_array.size(); i++) {
|
for (int i = 0; i < opt_array.size(); i++) {
|
||||||
@ -57,7 +54,7 @@ void ElementOptionTypeRadioTabs::init_option(std::string& _config_key) {
|
|||||||
const std::string opt_id = radio_input_id + config_key + "--" + opt_val;
|
const std::string opt_id = radio_input_id + config_key + "--" + opt_val;
|
||||||
|
|
||||||
const std::string translation_key = "translations/" + config_key + "/values/" + opt_val;
|
const std::string translation_key = "translations/" + config_key + "/values/" + opt_val;
|
||||||
const std::string& opt_text = recomp::get_config_store_value<std::string>(translation_key);
|
const std::string& opt_text = recomp::config::get_config_store_value<std::string>(translation_key);
|
||||||
|
|
||||||
Rml::Element *radio_el = AppendChild(GetOwnerDocument()->CreateElement("input"));
|
Rml::Element *radio_el = AppendChild(GetOwnerDocument()->CreateElement("input"));
|
||||||
|
|
||||||
@ -88,7 +85,7 @@ void ElementOptionTypeRadioTabs::ProcessEvent(Rml::Event& event)
|
|||||||
Rml::Element *target = event.GetTargetElement();
|
Rml::Element *target = event.GetTargetElement();
|
||||||
auto val_variant = target->GetAttribute("value");
|
auto val_variant = target->GetAttribute("value");
|
||||||
int new_value = val_variant->Get<int>();
|
int new_value = val_variant->Get<int>();
|
||||||
recomp::set_config_store_value(config_key, new_value);
|
recomp::config::set_config_store_value(config_key, new_value);
|
||||||
set_cur_option(new_value);
|
set_cur_option(new_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_RADIO_TABS_H
|
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_RADIO_TABS_H
|
||||||
#define RECOMPUI_ELEMENT_OPTION_TYPE_RADIO_TABS_H
|
#define RECOMPUI_ELEMENT_OPTION_TYPE_RADIO_TABS_H
|
||||||
|
|
||||||
#include "RmlUi/Core/Element.h"
|
#include "common.h"
|
||||||
#include "RmlUi/Core/Elements/ElementFormControlInput.h"
|
|
||||||
#include "RmlUi/Core/EventListener.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
#include "ElementOptionTypeRange.h"
|
#include "ElementOptionTypeRange.h"
|
||||||
#include "librecomp/config_store.hpp"
|
|
||||||
#include "../config_options/ConfigRegistry.h"
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <RmlUi/Core/ElementDocument.h>
|
#include <RmlUi/Core/ElementDocument.h>
|
||||||
#include <RmlUi/Core/ElementText.h>
|
#include <RmlUi/Core/ElementText.h>
|
||||||
@ -58,13 +57,13 @@ void ElementOptionTypeRange::set_value_label(int value) {
|
|||||||
|
|
||||||
void ElementOptionTypeRange::init_option(std::string& _config_key) {
|
void ElementOptionTypeRange::init_option(std::string& _config_key) {
|
||||||
config_key = _config_key;
|
config_key = _config_key;
|
||||||
const json& option_json = get_json_from_key(config_key);
|
const json& option_json = recomp::config::get_json_from_key(config_key);
|
||||||
|
|
||||||
const int value = recomp::get_config_store_value<int>(config_key);
|
const int value = recomp::config::get_config_store_value<int>(config_key);
|
||||||
suffix = get_string_in_json_with_default(option_json, "suffix", "");
|
suffix = recomp::config::get_string_in_json_with_default(option_json, "suffix", "");
|
||||||
const int min = get_value_in_json<int>(option_json, "min");
|
const int min = recomp::config::get_value_in_json<int>(option_json, "min");
|
||||||
const int max = get_value_in_json<int>(option_json, "max");
|
const int max = recomp::config::get_value_in_json<int>(option_json, "max");
|
||||||
const int step = get_value_in_json_with_default<int>(option_json, "step", 1);
|
const int step = recomp::config::get_value_in_json_with_default<int>(option_json, "step", 1);
|
||||||
|
|
||||||
Rml::ElementFormControlInput *range = (Rml::ElementFormControlInput *)GetElementById(range_input_id);
|
Rml::ElementFormControlInput *range = (Rml::ElementFormControlInput *)GetElementById(range_input_id);
|
||||||
range->SetAttribute("min", min);
|
range->SetAttribute("min", min);
|
||||||
@ -77,7 +76,6 @@ void ElementOptionTypeRange::init_option(std::string& _config_key) {
|
|||||||
|
|
||||||
void ElementOptionTypeRange::ProcessEvent(Rml::Event& event)
|
void ElementOptionTypeRange::ProcessEvent(Rml::Event& event)
|
||||||
{
|
{
|
||||||
// Forward clicks to the target.
|
|
||||||
if (event == Rml::EventId::Change)
|
if (event == Rml::EventId::Change)
|
||||||
{
|
{
|
||||||
if (event.GetPhase() == Rml::EventPhase::Bubble || event.GetPhase() == Rml::EventPhase::Target)
|
if (event.GetPhase() == Rml::EventPhase::Bubble || event.GetPhase() == Rml::EventPhase::Target)
|
||||||
@ -85,7 +83,7 @@ void ElementOptionTypeRange::ProcessEvent(Rml::Event& event)
|
|||||||
Rml::ElementFormControlInput *target = (Rml::ElementFormControlInput *)event.GetTargetElement();
|
Rml::ElementFormControlInput *target = (Rml::ElementFormControlInput *)event.GetTargetElement();
|
||||||
auto val_s = target->GetValue();
|
auto val_s = target->GetValue();
|
||||||
int new_value = std::stoi(val_s);
|
int new_value = std::stoi(val_s);
|
||||||
recomp::set_config_store_value(config_key, new_value);
|
recomp::config::set_config_store_value(config_key, new_value);
|
||||||
set_value_label(new_value);
|
set_value_label(new_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_RANGE_H
|
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_RANGE_H
|
||||||
#define RECOMPUI_ELEMENT_OPTION_TYPE_RANGE_H
|
#define RECOMPUI_ELEMENT_OPTION_TYPE_RANGE_H
|
||||||
|
|
||||||
#include "RmlUi/Core/Element.h"
|
#include "common.h"
|
||||||
#include "RmlUi/Core/Elements/ElementFormControlInput.h"
|
|
||||||
#include "RmlUi/Core/EventListener.h"
|
|
||||||
|
|
||||||
namespace recompui {
|
namespace recompui {
|
||||||
|
|
||||||
|
10
src/ui/elements/common.h
Normal file
10
src/ui/elements/common.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef RECOMPUI_ELEMENTS_COMMON
|
||||||
|
#define RECOMPUI_ELEMENTS_COMMON
|
||||||
|
// Common includes for custom recomp elements
|
||||||
|
|
||||||
|
#include "recomp_ui.h"
|
||||||
|
#include "librecomp/config.hpp"
|
||||||
|
#include "json/json.hpp"
|
||||||
|
#include "RmlUi/Core.h"
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
#include "ui_elements.h"
|
#include "ui_elements.h"
|
||||||
#include "librecomp/config_store.hpp"
|
#include "librecomp/config.hpp"
|
||||||
|
|
||||||
struct RecompElementConfig {
|
struct RecompElementConfig {
|
||||||
Rml::String tag;
|
Rml::String tag;
|
||||||
@ -13,14 +13,15 @@ static RecompElementConfig custom_elements[] = {
|
|||||||
CUSTOM_ELEMENT("recomp-config-group", recompui::ElementConfigGroup),
|
CUSTOM_ELEMENT("recomp-config-group", recompui::ElementConfigGroup),
|
||||||
CUSTOM_ELEMENT("recomp-config-option", recompui::ElementConfigOption),
|
CUSTOM_ELEMENT("recomp-config-option", recompui::ElementConfigOption),
|
||||||
CUSTOM_ELEMENT("recomp-option-type-checkbox", recompui::ElementOptionTypeCheckbox),
|
CUSTOM_ELEMENT("recomp-option-type-checkbox", recompui::ElementOptionTypeCheckbox),
|
||||||
|
CUSTOM_ELEMENT("recomp-option-type-color", recompui::ElementOptionTypeColor),
|
||||||
CUSTOM_ELEMENT("recomp-option-type-radio-tabs", recompui::ElementOptionTypeRadioTabs),
|
CUSTOM_ELEMENT("recomp-option-type-radio-tabs", recompui::ElementOptionTypeRadioTabs),
|
||||||
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
CUSTOM_ELEMENT("recomp-option-type-range", recompui::ElementOptionTypeRange),
|
||||||
};
|
};
|
||||||
|
|
||||||
void recompui::register_custom_elements() {
|
void recompui::register_custom_elements() {
|
||||||
recomp::set_config_store_value_and_default("ligma_balls", "hello!", "whats up");
|
recomp::config::set_config_store_value_and_default("ligma_balls", "hello!", "whats up");
|
||||||
recomp::set_config_store_default_value("ligma_balls2", "12345");
|
recomp::config::set_config_store_default_value("ligma_balls2", "12345");
|
||||||
recomp::set_config_store_value("ligma_balls3", "hello!");
|
recomp::config::set_config_store_value("ligma_balls3", "hello!");
|
||||||
for (auto& element_config : custom_elements) {
|
for (auto& element_config : custom_elements) {
|
||||||
Rml::Factory::RegisterElementInstancer(element_config.tag, element_config.instancer.get());
|
Rml::Factory::RegisterElementInstancer(element_config.tag, element_config.instancer.get());
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "elements/ElementConfigOption.h"
|
#include "elements/ElementConfigOption.h"
|
||||||
#include "elements/ElementConfigGroup.h"
|
#include "elements/ElementConfigGroup.h"
|
||||||
#include "elements/ElementOptionTypeCheckbox.h"
|
#include "elements/ElementOptionTypeCheckbox.h"
|
||||||
|
#include "elements/ElementOptionTypeColor.h"
|
||||||
#include "elements/ElementOptionTypeRadioTabs.h"
|
#include "elements/ElementOptionTypeRadioTabs.h"
|
||||||
#include "elements/ElementOptionTypeRange.h"
|
#include "elements/ElementOptionTypeRange.h"
|
||||||
#include "elements/ElementDescription.h"
|
#include "elements/ElementDescription.h"
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#include "RmlUi_Platform_SDL.h"
|
#include "RmlUi_Platform_SDL.h"
|
||||||
|
|
||||||
#include "ui_elements.h"
|
#include "ui_elements.h"
|
||||||
#include "config_options/ConfigRegistry.h"
|
#include "librecomp/config.hpp"
|
||||||
|
|
||||||
#include "InterfaceVS.hlsl.spirv.h"
|
#include "InterfaceVS.hlsl.spirv.h"
|
||||||
#include "InterfacePS.hlsl.spirv.h"
|
#include "InterfacePS.hlsl.spirv.h"
|
||||||
@ -1149,11 +1149,11 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
|||||||
std::filesystem::path test_conf_trans_path = "config_example.cheats.en_us.json";
|
std::filesystem::path test_conf_trans_path = "config_example.cheats.en_us.json";
|
||||||
if (std::filesystem::exists(test_conf_path)) {
|
if (std::filesystem::exists(test_conf_path)) {
|
||||||
const std::string s = read_file_to_string(test_conf_path);
|
const std::string s = read_file_to_string(test_conf_path);
|
||||||
recompui::register_config(read_file_to_string(test_conf_path), "cheats");
|
recomp::config::register_config(read_file_to_string(test_conf_path), "cheats");
|
||||||
printf("SUCC CONF\n");
|
printf("SUCC CONF\n");
|
||||||
|
|
||||||
if (std::filesystem::exists(test_conf_trans_path)) {
|
if (std::filesystem::exists(test_conf_trans_path)) {
|
||||||
recompui::register_translation(read_file_to_string("config_example.cheats.en_us.json"), "cheats");
|
recomp::config::register_translation(read_file_to_string("config_example.cheats.en_us.json"), "cheats");
|
||||||
printf("SUCC TRANSLATION\n");
|
printf("SUCC TRANSLATION\n");
|
||||||
} else {
|
} else {
|
||||||
printf("FAIL TRANSLATION");
|
printf("FAIL TRANSLATION");
|
||||||
|
145
src/ui/util/hsv.cpp
Normal file
145
src/ui/util/hsv.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
#include "hsv.h"
|
||||||
|
#include <algorithm> // std::min, std::max and std::clamp
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
|
||||||
|
void HsvToRgb(HsvColor& hsv, RgbColor& rgb)
|
||||||
|
{
|
||||||
|
unsigned char region, remainder, p, q, t;
|
||||||
|
|
||||||
|
if (hsv.s == 0)
|
||||||
|
{
|
||||||
|
rgb.r = hsv.v;
|
||||||
|
rgb.g = hsv.v;
|
||||||
|
rgb.b = hsv.v;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
region = hsv.h / 43;
|
||||||
|
remainder = (hsv.h - (region * 43)) * 6;
|
||||||
|
|
||||||
|
p = (hsv.v * (255 - hsv.s)) >> 8;
|
||||||
|
q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
|
||||||
|
t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
|
||||||
|
|
||||||
|
switch (region)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
rgb.r = hsv.v; rgb.g = t; rgb.b = p;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
rgb.r = q; rgb.g = hsv.v; rgb.b = p;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
rgb.r = p; rgb.g = hsv.v; rgb.b = t;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
rgb.r = p; rgb.g = q; rgb.b = hsv.v;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
rgb.r = t; rgb.g = p; rgb.b = hsv.v;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rgb.r = hsv.v; rgb.g = p; rgb.b = q;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RgbToHsv(RgbColor& rgb, HsvColor& hsv)
|
||||||
|
{
|
||||||
|
unsigned char rgbMin, rgbMax;
|
||||||
|
|
||||||
|
rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
|
||||||
|
rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
|
||||||
|
|
||||||
|
hsv.v = rgbMax;
|
||||||
|
if (hsv.v == 0)
|
||||||
|
{
|
||||||
|
hsv.h = 0;
|
||||||
|
hsv.s = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
|
||||||
|
if (hsv.s == 0)
|
||||||
|
{
|
||||||
|
hsv.h = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rgbMax == rgb.r)
|
||||||
|
hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
|
||||||
|
else if (rgbMax == rgb.g)
|
||||||
|
hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
|
||||||
|
else
|
||||||
|
hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char clamp_255(float f) {
|
||||||
|
return std::clamp((int)round(f), 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HsvFToRgb(HsvColorF in, RgbColor& out)
|
||||||
|
{
|
||||||
|
float hh, p, q, t, ff;
|
||||||
|
long i;
|
||||||
|
|
||||||
|
unsigned char val = clamp_255(in.v * 255.0f);
|
||||||
|
|
||||||
|
if (in.s <= 0.0) {
|
||||||
|
out.r = val;
|
||||||
|
out.g = val;
|
||||||
|
out.b = val;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hh = in.h;
|
||||||
|
if (hh >= 360.0f) hh = 0.0f;
|
||||||
|
hh /= 60.0f;
|
||||||
|
i = (float)hh;
|
||||||
|
ff = hh - i;
|
||||||
|
p = in.v * (1.0f - in.s);
|
||||||
|
q = in.v * (1.0f - (in.s * ff));
|
||||||
|
t = in.v * (1.0f - (in.s * (1.0f - ff)));
|
||||||
|
|
||||||
|
unsigned char up = clamp_255(p * 255.0f);
|
||||||
|
unsigned char uq = clamp_255(q * 255.0f);
|
||||||
|
unsigned char ut = clamp_255(t * 255.0f);
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
out.r = val;
|
||||||
|
out.g = ut;
|
||||||
|
out.b = up;
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
out.r = uq;
|
||||||
|
out.g = val;
|
||||||
|
out.b = up;
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
out.r = up;
|
||||||
|
out.g = val;
|
||||||
|
out.b = ut;
|
||||||
|
return;
|
||||||
|
case 3:
|
||||||
|
out.r = up;
|
||||||
|
out.g = uq;
|
||||||
|
out.b = val;
|
||||||
|
return;
|
||||||
|
case 4:
|
||||||
|
out.r = ut;
|
||||||
|
out.g = up;
|
||||||
|
out.b = val;
|
||||||
|
return;
|
||||||
|
case 5:
|
||||||
|
default:
|
||||||
|
out.r = val;
|
||||||
|
out.g = up;
|
||||||
|
out.b = uq;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
77
src/ui/util/hsv.h
Normal file
77
src/ui/util/hsv.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef RECOMP_UI_HSV
|
||||||
|
#define RECOMP_UI_HSV
|
||||||
|
|
||||||
|
|
||||||
|
namespace recompui {
|
||||||
|
typedef struct RgbColor
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
unsigned char r;
|
||||||
|
unsigned char g;
|
||||||
|
unsigned char b;
|
||||||
|
};
|
||||||
|
unsigned char data[3]; // Array access
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operator[] to access members by index
|
||||||
|
unsigned char& operator[](int index) {
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const version for read-only access
|
||||||
|
const unsigned char& operator[](int index) const {
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
} RgbColor;
|
||||||
|
|
||||||
|
typedef struct HsvColor
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
unsigned char h;
|
||||||
|
unsigned char s;
|
||||||
|
unsigned char v;
|
||||||
|
};
|
||||||
|
unsigned char data[3]; // Array access
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operator[] to access members by index
|
||||||
|
unsigned char& operator[](int index) {
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const version for read-only access
|
||||||
|
const unsigned char& operator[](int index) const {
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
} HsvColor;
|
||||||
|
|
||||||
|
typedef struct HsvColorF
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
float h; // 0-360
|
||||||
|
float s; // 0-1
|
||||||
|
float v; // 0-1
|
||||||
|
};
|
||||||
|
float data[3]; // Array access
|
||||||
|
};
|
||||||
|
|
||||||
|
// Operator[] to access members by index
|
||||||
|
float& operator[](int index) {
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const version for read-only access
|
||||||
|
const float& operator[](int index) const {
|
||||||
|
return data[index];
|
||||||
|
}
|
||||||
|
} HsvColorF;
|
||||||
|
|
||||||
|
void HsvToRgb(HsvColor& hsv, RgbColor& rgb);
|
||||||
|
void HsvFToRgb(HsvColorF in, RgbColor& out);
|
||||||
|
void RgbToHsv(RgbColor& rgb, HsvColor& hsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user