mirror of
https://github.com/Zelda64Recomp/Zelda64Recomp
synced 2026-07-04 21:35:51 -04:00
init config opt system w/ 3 types and description support
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
|
||||
#include "ElementConfigGroup.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 "recomp_ui.h"
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
#include <RmlUi/Core/ElementText.h>
|
||||
#include <cassert>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string config_group_base_class = "config-group";
|
||||
static const std::string config_group_base_class_scrollable = config_group_base_class + "--scrollable";
|
||||
static const std::string config_group_title_class = config_group_base_class + "__title";
|
||||
static const std::string config_group_title_class_hidden = config_group_title_class + "--hidden";
|
||||
static const std::string config_group_wrapper_class = config_group_base_class + "__wrapper";
|
||||
|
||||
ElementConfigGroup::ElementConfigGroup(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
SetAttribute("recomp-store-element", true);
|
||||
SetClass(config_group_base_class, true);
|
||||
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
|
||||
{
|
||||
Rml::Element *title_el = AppendChild(doc->CreateElement("div"));
|
||||
title_el->SetClass(config_group_title_class, true);
|
||||
{
|
||||
Rml::Element *text_el = title_el->AppendChild(doc->CreateTextNode("replace me"));
|
||||
text_el->SetId("config-group-label");
|
||||
} // title_el
|
||||
|
||||
Rml::Element *div_el = AppendChild(doc->CreateElement("div"));
|
||||
div_el->SetClass(config_group_wrapper_class, true);
|
||||
}
|
||||
}
|
||||
|
||||
ElementConfigGroup::~ElementConfigGroup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ElementConfigGroup::SetTextLabel(const std::string& s) {
|
||||
Rml::Element *title_container = GetChild(0);
|
||||
Rml::ElementText *label = (Rml::ElementText *)title_container->GetElementById("config-group-label");
|
||||
label->SetText(s);
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
void ElementConfigGroup::ToggleTextLabelVisibility(bool show) {
|
||||
Rml::Element *title_container = GetChild(0);
|
||||
title_container->SetClass(config_group_title_class_hidden, !show);
|
||||
}
|
||||
|
||||
void ElementConfigGroup::AddConfigOptionElement(const json& option_json) {
|
||||
ConfigOptionType el_option_type = ConfigOptionType::Label;
|
||||
from_json(option_json["type"], el_option_type);
|
||||
|
||||
const std::string key = get_string_in_json(option_json, ConfigOption::schema::key);
|
||||
|
||||
Rml::Element *option_container = GetChild(1);
|
||||
Rml::Element *child_opt = nullptr;
|
||||
|
||||
if (config_option_is_group(el_option_type)) {
|
||||
child_opt = (ElementConfigGroup *)option_container->AppendChild(
|
||||
recompui::create_custom_element(GetOwnerDocument(), "recomp-config-group")
|
||||
);
|
||||
// TODO: Base class with option type + config_key
|
||||
((ElementConfigGroup *)child_opt)->option_type = el_option_type;
|
||||
} else {
|
||||
child_opt = (ElementConfigOption *)option_container->AppendChild(
|
||||
recompui::create_custom_element(GetOwnerDocument(), "recomp-config-option")
|
||||
);
|
||||
// TODO: Base class with option type + config_key
|
||||
ElementConfigOption *opt_as_conf_opt = (ElementConfigOption *)child_opt;
|
||||
opt_as_conf_opt->option_type = el_option_type;
|
||||
opt_as_conf_opt->in_checkbox_group = option_type == ConfigOptionType::CheckboxGroup;
|
||||
}
|
||||
std::string full_key = config_key + "/" + key;
|
||||
child_opt->SetAttribute("recomp-data", full_key);
|
||||
}
|
||||
|
||||
static nlohmann::json get_options(std::string& config_key) {
|
||||
if (config_key_is_base_group(config_key)) {
|
||||
return get_group_json(config_key);
|
||||
}
|
||||
|
||||
const json& group_json = get_json_from_key(config_key);
|
||||
return group_json["options"];
|
||||
}
|
||||
|
||||
void ElementConfigGroup::OnAttributeChange(const Rml::ElementAttributes& changed_attributes)
|
||||
{
|
||||
// Call through to the base element's OnAttributeChange().
|
||||
Rml::Element::OnAttributeChange(changed_attributes);
|
||||
|
||||
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) {
|
||||
config_key = config_store_key_attr->second.Get<Rml::String>();
|
||||
bool is_base_group = config_key_is_base_group(config_key);
|
||||
|
||||
option_type = ConfigOptionType::Label;
|
||||
|
||||
if (is_base_group) {
|
||||
SetClass(config_group_base_class_scrollable, true);
|
||||
option_type = ConfigOptionType::Group;
|
||||
ToggleTextLabelVisibility(false);
|
||||
} else {
|
||||
try {
|
||||
auto value = recomp::get_config_store_value<std::string>("translations/" + config_key);
|
||||
SetTextLabel(value);
|
||||
} catch (const std::runtime_error& e) {
|
||||
SetTextLabel(e.what());
|
||||
}
|
||||
|
||||
const json& group_json = get_json_from_key(config_key);
|
||||
|
||||
from_json(group_json["type"], option_type);
|
||||
assert(
|
||||
option_type == ConfigOptionType::Group || option_type == ConfigOptionType::CheckboxGroup);
|
||||
}
|
||||
|
||||
const nlohmann::json& options = get_options(config_key);
|
||||
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
const auto &el = options[i];
|
||||
AddConfigOptionElement(el);
|
||||
}
|
||||
DirtyLayout();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rml
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef RECOMPUI_ELEMENTS_CONFIG_GROUP_H
|
||||
#define RECOMPUI_ELEMENTS_CONFIG_GROUP_H
|
||||
|
||||
#include "RmlUi/Core/Element.h"
|
||||
#include "json/json.hpp"
|
||||
#include "../config_options/ConfigOption.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
/**
|
||||
Base custom element representing a single config option.
|
||||
Maps to other custom elements.
|
||||
*/
|
||||
|
||||
class ElementConfigGroup : public Rml::Element {
|
||||
public:
|
||||
ElementConfigGroup(const Rml::String& tag);
|
||||
virtual ~ElementConfigGroup();
|
||||
|
||||
ConfigOptionType option_type;
|
||||
std::string config_key;
|
||||
protected:
|
||||
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
||||
void AddConfigOptionElement(const nlohmann::json& option_json);
|
||||
void SetTextLabel(const std::string& s);
|
||||
void ToggleTextLabelVisibility(bool show);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
|
||||
#include "ElementConfigOption.h"
|
||||
#include "../config_options/ConfigOption.h"
|
||||
#include "../config_options/ConfigRegistry.h"
|
||||
#include "ElementOptionTypeCheckbox.h"
|
||||
#include "ElementOptionTypeRadioTabs.h"
|
||||
#include "ElementOptionTypeRange.h"
|
||||
#include "librecomp/config_store.hpp"
|
||||
#include <string>
|
||||
#include "recomp_ui.h"
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
#include <RmlUi/Core/ElementText.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string option_type_wrapper_id = "checkbox__input";
|
||||
|
||||
static const std::string config_option_base_class = "config-option";
|
||||
static const std::string config_option_base_class_hz = config_option_base_class + "--hz";
|
||||
static const std::string config_option_title_class = config_option_base_class + "__title";
|
||||
static const std::string config_option_wrapper = config_option_base_class + "__list"; // TODO: move hz listing to radio tabs, make this container dumber
|
||||
|
||||
ElementConfigOption::ElementConfigOption(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
|
||||
SetAttribute("recomp-store-element", true);
|
||||
SetClass(config_option_base_class, true);
|
||||
|
||||
{
|
||||
Rml::Element *label_el = AppendChild(doc->CreateElement("label"));
|
||||
label_el->SetClass(config_option_title_class, true);
|
||||
{
|
||||
Rml::Element *label_text_el = label_el->AppendChild(doc->CreateTextNode("Unknown"));
|
||||
label_text_el->SetAttribute("id", "config-opt-label");
|
||||
} // label_el
|
||||
|
||||
Rml::Element *option_type_wrapper = AppendChild(doc->CreateElement("div"));
|
||||
option_type_wrapper->SetId(option_type_wrapper_id);
|
||||
option_type_wrapper->SetClass(config_option_wrapper, true);
|
||||
} // base element
|
||||
|
||||
AddEventListener(Rml::EventId::Mouseover, this, true);
|
||||
}
|
||||
|
||||
ElementConfigOption::~ElementConfigOption()
|
||||
{
|
||||
RemoveEventListener(Rml::EventId::Mouseover, this, true);
|
||||
}
|
||||
|
||||
void ElementConfigOption::SetTextLabel(const std::string& s) {
|
||||
Rml::ElementText *label = (Rml::ElementText *)GetElementById("config-opt-label");
|
||||
label->SetText(s);
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
Rml::Element *ElementConfigOption::GetOptionTypeWrapper() {
|
||||
return GetElementById(option_type_wrapper_id);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void add_option_el(Rml::ElementDocument *doc, Rml::Element *wrapper, const std::string& tag, std::string& config_key) {
|
||||
T *opt = (T *)wrapper->AppendChild(recompui::create_custom_element(doc, tag));
|
||||
opt->init_option(config_key);
|
||||
}
|
||||
|
||||
void ElementConfigOption::AddOptionTypeElement() {
|
||||
ConfigOptionType el_option_type = ConfigOptionType::Label;
|
||||
const json& option_json = get_json_from_key(config_key);
|
||||
from_json(option_json["type"], el_option_type);
|
||||
|
||||
Rml::Element *wrapper = GetOptionTypeWrapper();
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
|
||||
switch (el_option_type) {
|
||||
default:
|
||||
printf("No option type element exists for type '%d'\n", el_option_type);
|
||||
return;
|
||||
case ConfigOptionType::Checkbox: {
|
||||
add_option_el<ElementOptionTypeCheckbox>(doc, wrapper, "recomp-option-type-checkbox", config_key);
|
||||
break;
|
||||
}
|
||||
case ConfigOptionType::RadioTabs: {
|
||||
add_option_el<ElementOptionTypeRadioTabs>(doc, wrapper, "recomp-option-type-radio-tabs", config_key);
|
||||
break;
|
||||
}
|
||||
case ConfigOptionType::Range: {
|
||||
add_option_el<ElementOptionTypeRange>(doc, wrapper, "recomp-option-type-range", config_key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ElementConfigOption::OnAttributeChange(const Rml::ElementAttributes& changed_attributes)
|
||||
{
|
||||
// Call through to the base element's OnAttributeChange().
|
||||
Rml::Element::OnAttributeChange(changed_attributes);
|
||||
|
||||
bool dirty_layout = false;
|
||||
|
||||
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) {
|
||||
config_key = config_store_key_attr->second.Get<Rml::String>();
|
||||
if (in_checkbox_group) {
|
||||
SetClass(config_option_base_class_hz, true);
|
||||
}
|
||||
|
||||
try {
|
||||
auto value = recomp::get_config_store_value<std::string>("translations/" + config_key);
|
||||
SetTextLabel(value);
|
||||
printf("found type and translation\n");
|
||||
AddOptionTypeElement();
|
||||
} catch (const std::runtime_error& e) {
|
||||
SetTextLabel(e.what());
|
||||
}
|
||||
dirty_layout = true;
|
||||
}
|
||||
|
||||
if (dirty_layout) {
|
||||
DirtyLayout();
|
||||
}
|
||||
}
|
||||
|
||||
void ElementConfigOption::ProcessEvent(Rml::Event& event)
|
||||
{
|
||||
// Set description key
|
||||
if (event == Rml::EventId::Mouseover)
|
||||
{
|
||||
if (event.GetPhase() == Rml::EventPhase::Capture || event.GetPhase() == Rml::EventPhase::Target)
|
||||
{
|
||||
Rml::ElementList elements;
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
GetElementsByTagName(elements, "input");
|
||||
doc->GetElementsByTagName(elements, "recomp-description");
|
||||
for (size_t i = 0; i < elements.size(); i++) {
|
||||
Rml::Element *el = elements[i];
|
||||
el->SetAttribute("recomp-data", config_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rml
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef RECOMPUI_ELEMENTS_CONFIG_OPTION_H
|
||||
#define RECOMPUI_ELEMENTS_CONFIG_OPTION_H
|
||||
|
||||
#include "RmlUi/Core/Element.h"
|
||||
#include "../config_options/ConfigOption.h"
|
||||
#include "RmlUi/Core/EventListener.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
/**
|
||||
Base custom element representing a single config option.
|
||||
Maps to other custom elements.
|
||||
*/
|
||||
|
||||
class ElementConfigOption : public Rml::Element, public Rml::EventListener {
|
||||
public:
|
||||
ElementConfigOption(const Rml::String& tag);
|
||||
virtual ~ElementConfigOption();
|
||||
|
||||
ConfigOptionType option_type;
|
||||
std::string config_key;
|
||||
bool in_checkbox_group = false;
|
||||
|
||||
protected:
|
||||
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
||||
void SetTextLabel(const std::string& s);
|
||||
void AddOptionTypeElement();
|
||||
Rml::Element *GetOptionTypeWrapper();
|
||||
void ProcessEvent(Rml::Event& event) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
#include "ElementDescription.h"
|
||||
#include "librecomp/config_store.hpp"
|
||||
#include "../config_options/ConfigRegistry.h"
|
||||
#include <string>
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
#include <RmlUi/Core/ElementText.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string cls_base = "config-description";
|
||||
static const std::string cls_contents = cls_base + "__contents";
|
||||
|
||||
static const std::string contents_id = cls_base + "__contents-text";
|
||||
|
||||
static const std::string default_contents = "";
|
||||
|
||||
ElementDescription::ElementDescription(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
SetAttribute("recomp-store-element", true);
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
|
||||
SetClass(cls_base, true);
|
||||
{
|
||||
Rml::Element *p_el = AppendChild(doc->CreateElement("p"));
|
||||
p_el->SetClass(cls_contents, true);
|
||||
p_el->SetId(contents_id);
|
||||
}
|
||||
}
|
||||
|
||||
ElementDescription::~ElementDescription()
|
||||
{
|
||||
}
|
||||
|
||||
void ElementDescription::update_text(const std::string& text) {
|
||||
auto *p_el = GetElementById(contents_id);
|
||||
p_el->SetInnerRML(text);
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
void ElementDescription::OnAttributeChange(const Rml::ElementAttributes& changed_attributes)
|
||||
{
|
||||
// Call through to the base element's OnAttributeChange().
|
||||
Rml::Element::OnAttributeChange(changed_attributes);
|
||||
|
||||
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) {
|
||||
config_key = config_store_key_attr->second.Get<Rml::String>();
|
||||
|
||||
try {
|
||||
auto value = recomp::get_config_store_value<std::string>("translations/" + config_key + ":description");
|
||||
update_text(value);
|
||||
} catch (const std::runtime_error& e) {
|
||||
update_text(default_contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rml
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef RECOMPUI_ELEMENT_DESCRIPTION_H
|
||||
#define RECOMPUI_ELEMENT_DESCRIPTION_H
|
||||
|
||||
#include "RmlUi/Core/Element.h"
|
||||
#include "../config_options/ConfigOption.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class ElementDescription : public Rml::Element {
|
||||
public:
|
||||
ElementDescription(const Rml::String& tag);
|
||||
virtual ~ElementDescription();
|
||||
|
||||
std::string config_key;
|
||||
protected:
|
||||
void update_text(const std::string& text_rml);
|
||||
private:
|
||||
void OnAttributeChange(const Rml::ElementAttributes& changed_attributes);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
#include "ElementOptionTypeCheckbox.h"
|
||||
#include "librecomp/config_store.hpp"
|
||||
#include "../config_options/ConfigRegistry.h"
|
||||
#include <string>
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
#include <RmlUi/Core/ElementText.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string checkbox_input_id = "checkbox__input";
|
||||
|
||||
ElementOptionTypeCheckbox::ElementOptionTypeCheckbox(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
SetAttribute("recomp-store-element", true);
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
|
||||
SetClass("config-option__checkbox-wrapper", true);
|
||||
{
|
||||
Rml::Element *checkbox_el = AppendChild(doc->CreateElement("input"));
|
||||
checkbox_el->SetId(checkbox_input_id);
|
||||
checkbox_el->SetClass("config-option__checkbox", true);
|
||||
checkbox_el->SetAttribute("type", "checkbox");
|
||||
}
|
||||
AddEventListener(Rml::EventId::Click, this, true);
|
||||
}
|
||||
|
||||
ElementOptionTypeCheckbox::~ElementOptionTypeCheckbox()
|
||||
{
|
||||
RemoveEventListener(Rml::EventId::Click, this, true);
|
||||
}
|
||||
|
||||
Rml::ElementFormControlInput *ElementOptionTypeCheckbox::get_input() {
|
||||
return (Rml::ElementFormControlInput *)GetElementById(checkbox_input_id);
|
||||
}
|
||||
|
||||
void ElementOptionTypeCheckbox::set_checked(bool checked) {
|
||||
auto *input_el = get_input();
|
||||
|
||||
if (checked) {
|
||||
SetAttribute("checked", true);
|
||||
input_el->SetAttribute("checked", true);
|
||||
} else {
|
||||
RemoveAttribute("checked");
|
||||
input_el->RemoveAttribute("checked");
|
||||
}
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
void ElementOptionTypeCheckbox::init_option(std::string& _config_key) {
|
||||
config_key = _config_key;
|
||||
const json& option_json = get_json_from_key(config_key);
|
||||
int value = recomp::get_config_store_value<int>(config_key);
|
||||
set_checked(value);
|
||||
}
|
||||
|
||||
void ElementOptionTypeCheckbox::ProcessEvent(Rml::Event& event)
|
||||
{
|
||||
// Forward clicks to the target.
|
||||
if (event == Rml::EventId::Click && !disable_click)
|
||||
{
|
||||
if (event.GetPhase() == Rml::EventPhase::Capture || event.GetPhase() == Rml::EventPhase::Target)
|
||||
{
|
||||
bool new_value = !recomp::get_config_store_value<int>(config_key);
|
||||
recomp::set_config_store_value(config_key, new_value);
|
||||
set_checked(new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rml::Element* ElementOptionTypeCheckbox::GetTarget()
|
||||
{
|
||||
return get_input();
|
||||
}
|
||||
|
||||
} // namespace Rml
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_CHECKBOX_H
|
||||
#define RECOMPUI_ELEMENT_OPTION_TYPE_CHECKBOX_H
|
||||
|
||||
#include "RmlUi/Core/Element.h"
|
||||
#include "RmlUi/Core/Elements/ElementFormControlInput.h"
|
||||
#include "RmlUi/Core/EventListener.h"
|
||||
#include "../config_options/ConfigOption.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class ElementOptionTypeCheckbox : public Rml::Element, public Rml::EventListener {
|
||||
public:
|
||||
ElementOptionTypeCheckbox(const Rml::String& tag);
|
||||
virtual ~ElementOptionTypeCheckbox();
|
||||
|
||||
void init_option(std::string& _config_key);
|
||||
|
||||
std::string config_key;
|
||||
|
||||
const ConfigOptionType option_type = ConfigOptionType::Checkbox;
|
||||
protected:
|
||||
Rml::ElementFormControlInput *get_input();
|
||||
|
||||
void ProcessEvent(Rml::Event& event) override;
|
||||
|
||||
private:
|
||||
Element* GetTarget();
|
||||
void set_checked(bool checked);
|
||||
|
||||
bool disable_click = false;
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
#endif
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
#include "ElementOptionTypeRadioTabs.h"
|
||||
#include "librecomp/config_store.hpp"
|
||||
#include "../config_options/ConfigRegistry.h"
|
||||
#include <string>
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
#include <RmlUi/Core/ElementText.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string radio_input_id = "recomp-radio__";
|
||||
|
||||
ElementOptionTypeRadioTabs::ElementOptionTypeRadioTabs(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
SetAttribute("recomp-store-element", true);
|
||||
SetClass("config-option__radio-tabs", true);
|
||||
}
|
||||
|
||||
ElementOptionTypeRadioTabs::~ElementOptionTypeRadioTabs()
|
||||
{
|
||||
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 ElementOptionTypeRadioTabs::set_cur_option(int opt) {
|
||||
Rml::ElementList elements;
|
||||
GetElementsByTagName(elements, "input");
|
||||
for (int i = 0; i < elements.size(); i++) {
|
||||
Rml::Element *el = elements[i];
|
||||
if (i == opt) {
|
||||
SetAttribute("checked", true);
|
||||
el->SetAttribute("checked", true);
|
||||
} else {
|
||||
RemoveAttribute("checked");
|
||||
el->RemoveAttribute("checked");
|
||||
}
|
||||
}
|
||||
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
void ElementOptionTypeRadioTabs::init_option(std::string& _config_key) {
|
||||
config_key = _config_key;
|
||||
const json& option_json = get_json_from_key(config_key);
|
||||
int opt = recomp::get_config_store_value<int>(config_key);
|
||||
const json& opt_array = option_json["values"];
|
||||
|
||||
for (int i = 0; i < opt_array.size(); i++) {
|
||||
const auto &j_opt = opt_array[i];
|
||||
const std::string opt_val = j_opt.get<std::string>();
|
||||
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& opt_text = recomp::get_config_store_value<std::string>(translation_key);
|
||||
|
||||
Rml::Element *radio_el = AppendChild(GetOwnerDocument()->CreateElement("input"));
|
||||
|
||||
radio_el->SetId(opt_id);
|
||||
radio_el->SetAttribute("type", "radio");
|
||||
radio_el->SetAttribute("value", i);
|
||||
radio_el->AddEventListener(Rml::EventId::Click, this, false);
|
||||
// TODO: focus event set description
|
||||
// TODO: blur event clear description
|
||||
Rml::Element *label_el = AppendChild(GetOwnerDocument()->CreateElement("label"));
|
||||
label_el->SetAttribute("for", opt_id);
|
||||
label_el->SetClass("config-option__tab-label", true);
|
||||
{
|
||||
label_el->AppendChild(GetOwnerDocument()->CreateTextNode(opt_text));
|
||||
}
|
||||
}
|
||||
|
||||
set_cur_option(opt);
|
||||
}
|
||||
|
||||
void ElementOptionTypeRadioTabs::ProcessEvent(Rml::Event& event)
|
||||
{
|
||||
// Forward clicks to the target.
|
||||
if (event == Rml::EventId::Click)
|
||||
{
|
||||
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>();
|
||||
recomp::set_config_store_value(config_key, new_value);
|
||||
set_cur_option(new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rml
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_RADIO_TABS_H
|
||||
#define RECOMPUI_ELEMENT_OPTION_TYPE_RADIO_TABS_H
|
||||
|
||||
#include "RmlUi/Core/Element.h"
|
||||
#include "RmlUi/Core/Elements/ElementFormControlInput.h"
|
||||
#include "RmlUi/Core/EventListener.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class ElementOptionTypeRadioTabs : public Rml::Element, public Rml::EventListener {
|
||||
public:
|
||||
ElementOptionTypeRadioTabs(const Rml::String& tag);
|
||||
virtual ~ElementOptionTypeRadioTabs();
|
||||
|
||||
std::string config_key;
|
||||
void init_option(std::string& _config_key);
|
||||
protected:
|
||||
void ProcessEvent(Rml::Event& event) override;
|
||||
|
||||
private:
|
||||
void set_cur_option(int opt);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
|
||||
#include "ElementOptionTypeRange.h"
|
||||
#include "librecomp/config_store.hpp"
|
||||
#include "../config_options/ConfigRegistry.h"
|
||||
#include <string>
|
||||
#include <RmlUi/Core/ElementDocument.h>
|
||||
#include <RmlUi/Core/ElementText.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace recompui {
|
||||
|
||||
static const std::string range_label_id = "recomp-range__label";
|
||||
static const std::string range_label_text_id = "recomp-range__label-text";
|
||||
static const std::string range_input_id = "recomp-range__input";
|
||||
|
||||
static const std::string cls_base = "config-option-range";
|
||||
static const std::string cls_label = cls_base + "__label";
|
||||
static const std::string cls_range_input = cls_base + "__range-input";
|
||||
|
||||
ElementOptionTypeRange::ElementOptionTypeRange(const Rml::String& tag) : Rml::Element(tag)
|
||||
{
|
||||
SetAttribute("recomp-store-element", true);
|
||||
SetClass(cls_base, true);
|
||||
|
||||
Rml::ElementDocument *doc = GetOwnerDocument();
|
||||
|
||||
{
|
||||
Rml::Element *label_el = AppendChild(doc->CreateElement("label"));
|
||||
label_el->SetClass(cls_label, true);
|
||||
label_el->SetId(range_label_id);
|
||||
{
|
||||
Rml::Element *text_node = label_el->AppendChild(doc->CreateTextNode(""));
|
||||
text_node->SetId(range_label_text_id);
|
||||
}
|
||||
|
||||
Rml::Element *range_el = AppendChild(doc->CreateElement("input"));
|
||||
range_el->SetClass(cls_range_input, true);
|
||||
range_el->SetId(range_input_id);
|
||||
range_el->SetAttribute("type", "range");
|
||||
range_el->AddEventListener(Rml::EventId::Change, this, false);
|
||||
// TODO: focus event set description
|
||||
// TODO: blur event clear description
|
||||
}
|
||||
}
|
||||
|
||||
ElementOptionTypeRange::~ElementOptionTypeRange()
|
||||
{
|
||||
Rml::Element *range = GetElementById(range_input_id);
|
||||
range->RemoveEventListener(Rml::EventId::Change, this, false);
|
||||
}
|
||||
|
||||
void ElementOptionTypeRange::set_value_label(int value) {
|
||||
Rml::ElementText *text_label = (Rml::ElementText *)GetElementById(range_label_text_id);
|
||||
text_label->SetText(std::to_string(value) + suffix);
|
||||
DirtyLayout();
|
||||
}
|
||||
|
||||
void ElementOptionTypeRange::init_option(std::string& _config_key) {
|
||||
config_key = _config_key;
|
||||
const json& option_json = get_json_from_key(config_key);
|
||||
|
||||
const int value = recomp::get_config_store_value<int>(config_key);
|
||||
suffix = get_string_in_json_with_default(option_json, "suffix", "");
|
||||
const int min = get_value_in_json<int>(option_json, "min");
|
||||
const int max = get_value_in_json<int>(option_json, "max");
|
||||
const int step = get_value_in_json_with_default<int>(option_json, "step", 1);
|
||||
|
||||
Rml::ElementFormControlInput *range = (Rml::ElementFormControlInput *)GetElementById(range_input_id);
|
||||
range->SetAttribute("min", min);
|
||||
range->SetAttribute("max", max);
|
||||
range->SetAttribute("step", step);
|
||||
range->SetValue(std::to_string(value));
|
||||
|
||||
set_value_label(value);
|
||||
}
|
||||
|
||||
void ElementOptionTypeRange::ProcessEvent(Rml::Event& event)
|
||||
{
|
||||
// Forward clicks to the target.
|
||||
if (event == Rml::EventId::Change)
|
||||
{
|
||||
if (event.GetPhase() == Rml::EventPhase::Bubble || event.GetPhase() == Rml::EventPhase::Target)
|
||||
{
|
||||
Rml::ElementFormControlInput *target = (Rml::ElementFormControlInput *)event.GetTargetElement();
|
||||
auto val_s = target->GetValue();
|
||||
int new_value = std::stoi(val_s);
|
||||
recomp::set_config_store_value(config_key, new_value);
|
||||
set_value_label(new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rml
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef RECOMPUI_ELEMENT_OPTION_TYPE_RANGE_H
|
||||
#define RECOMPUI_ELEMENT_OPTION_TYPE_RANGE_H
|
||||
|
||||
#include "RmlUi/Core/Element.h"
|
||||
#include "RmlUi/Core/Elements/ElementFormControlInput.h"
|
||||
#include "RmlUi/Core/EventListener.h"
|
||||
|
||||
namespace recompui {
|
||||
|
||||
class ElementOptionTypeRange : public Rml::Element, public Rml::EventListener {
|
||||
public:
|
||||
ElementOptionTypeRange(const Rml::String& tag);
|
||||
virtual ~ElementOptionTypeRange();
|
||||
|
||||
std::string config_key;
|
||||
std::string suffix;
|
||||
|
||||
void init_option(std::string& _config_key);
|
||||
protected:
|
||||
void ProcessEvent(Rml::Event& event) override;
|
||||
|
||||
private:
|
||||
void set_cur_option(int opt);
|
||||
void set_value_label(int value);
|
||||
};
|
||||
|
||||
} // namespace recompui
|
||||
#endif
|
||||
Reference in New Issue
Block a user