Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 110 additions & 5 deletions src/imvue_element.h
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,6 @@ namespace ImVue {

if(!success && required) {
IMVUE_EXCEPTION(ElementError, "failed to read required attribute %s", attribute);
return;
}
}

Expand All @@ -768,6 +767,93 @@ namespace ImVue {
D C::* mMemPtr;
};

/**
* Setter implementation of attribute reader
*/
template<class C, typename Type>
class AttributeSetter : public Attribute {
public:

typedef void(C::*SetterFunc)(Type);

AttributeSetter(SetterFunc func, bool required = false)
: Attribute(required)
, mSetter(func)
{
static_assert(std::is_trivially_constructible<Type>::value, "setter can only be used with trivially constructible types");
}

void read(const char* attribute, const char* str, Element* element, ScriptState* scriptState, int flags = 0, ScriptState::Fields* fields = 0) const
{
Type value;
bool success = false;
if(scriptState) {
if(flags & SCRIPT) {
Object object = scriptState->getObject(str, fields, element->getContext());
if(!object.valid()) {
IMVUE_EXCEPTION(ScriptError, "failed to evaluate data %s", str);
return;
}
success = detail::read(object, &value);
} else if(flags & TEMPLATED_STRING) {
std::string retval;
std::stringstream result;
std::stringstream ss;
bool evaluation = false;

for(int i = 0;;i++) {
if(str[i] == '\0') {
break;
}

if(evaluation && std::strncmp(&str[i], "}}", 2) == 0) {
Object object = scriptState->getObject(&ss.str()[0], fields, element->getContext());
ss = std::stringstream();
evaluation = false;
result << object.as<ImString>().c_str();
i++;
continue;
}

if(!evaluation && std::strncmp(&str[i], "{{", 2) == 0) {
evaluation = true;
i++;
continue;
}

if(evaluation) {
ss << str[i];
} else {
result << str[i];
}
}

success = detail::read(&result.str()[0], &value);
}
}

if(!success) {
success = detail::read(str, &value);
}

if(success) {
(static_cast<C*>(element)->*mSetter)(value);
} else if(required) {
IMVUE_EXCEPTION(ElementError, "failed to read required attribute %s", attribute);
}
}

bool copy(Element* element, Object& dest) const
{
(void)element;
(void)dest;
return false;
}

private:
SetterFunc mSetter;
};

/**
* Handler factory interface
*/
Expand Down Expand Up @@ -899,23 +985,42 @@ namespace ImVue {
}

/**
* Register attribute reader
* Register attribute
*
* @param name attribute name
* @param memPtr destination to read into (&Element::variable)
* @param required property is required
*/
template<class D>
ElementBuilderImpl<C>& attribute(const char* name, D C::* memPtr, bool required = false)
template<class Type>
ElementBuilderImpl<C>& attribute(const char* name, Type C::* memPtr, bool required = false)
{
mAttributes[name] = new AttributeMemPtr<C, D>(memPtr, required);
mAttributes[name] = new AttributeMemPtr<C, Type>(memPtr, required);
mAttributes[name]->owner = this;
if(required) {
mRequiredAttrs.push_back(name);
}
return *this;
}

/**
* Register attribute setter
*
* @param name attribute name
* @param func setter function to use (&Element::setValue)
* @param required property is required
*/
template<class Type>
ElementBuilderImpl<C>& setter(const char* name, void(C::*func)(Type), bool required = false)
{
mAttributes[name] = new AttributeSetter<C, Type>(func, required);
mAttributes[name]->owner = this;
if(required) {
mRequiredAttrs.push_back(name);
}
return *this;
}


/**
* Register text reader
*
Expand Down
10 changes: 10 additions & 0 deletions tests/unit/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ class Validator : public ImVue::Element
{
}

void useSetter(int value)
{
fromSetter = value;
}

bool flag;
float fl;
double dbl;
Expand All @@ -134,6 +139,7 @@ class Validator : public ImVue::Element
char* ch;
int arrFixed[3];
int* arrVariadic;
int fromSetter;
#if defined(WITH_LUA)
ImVue::Object object;
#endif
Expand Down Expand Up @@ -179,6 +185,7 @@ TEST_P(ReadTypesTest, ParseTypes)
.attribute("vec4", &Validator::vec4, true)
.attribute("arr-fixed", &Validator::arrFixed, true)
.attribute("arr-variadic", &Validator::arrVariadic, true)
.setter("setter", &Validator::useSetter, true)
#if defined(WITH_LUA)
.attribute("object", &Validator::object)
#endif
Expand All @@ -205,6 +212,7 @@ TEST_P(ReadTypesTest, ParseTypes)
EXPECT_EQ(el->i16, -32765);
EXPECT_EQ(el->i32, -65536);
EXPECT_EQ(el->i64, -1073741823);
EXPECT_EQ(el->fromSetter, 100);
EXPECT_FLOAT_EQ(el->vec2.x, 2);
EXPECT_FLOAT_EQ(el->vec2.y, 2);

Expand Down Expand Up @@ -242,6 +250,7 @@ const char* dataStatic = "<template>"
"float='0.1' double='0.05' "
"ch='hello' "
"arr-fixed='{1,2,3}' arr-variadic='{1,2,3,4,5}' "
"setter='100' "
"/>"
"</template>";

Expand All @@ -255,6 +264,7 @@ const char* dataBind = "<template>"
":arr-fixed='{1,2,3}' :arr-variadic='{1,2,3,4,5}' "
":float='0.1' :double='0.05' "
":object='{it=\"works\"}'"
":setter='100' "
"/>"
"</template>"
"<script>"
Expand Down