Browse Source

Add data-class and data-rml

Michael Ragazzon 5 years ago
parent
commit
a66c97b4e0

+ 4 - 2
Include/RmlUi/Core/DataModel.h

@@ -136,13 +136,15 @@ public:
 	// Register an array type.
 	// @note The type applies to every data model associated with the current Context.
 	// @note If 'Container::value_type' represents a non-scalar type, that type must already have been registered with the appropriate 'Register...()' functions.
-	// @note Container requires the following functions implemented: size() and operator[]. This includes std::vector and std::array.
+	// @note Container requires the following functions implemented: size() and operator[]. This is satisfied by std::vector and std::array.
 	template<typename Container>
 	bool RegisterArray() {
 		return type_register->RegisterArray<Container>();
 	}
 
-
+	// Register a transform function.
+	// A transform function modifies a variant with optional arguments. It can be called in data expressions using the pipe '|' operator.
+	// @note The transform function applies to every data model associated with the current Context.
 	void RegisterTransformFunc(const String& name, DataTransformFunc transform_func) {
 		type_register->GetTransformFuncRegister()->Register(name, std::move(transform_func));
 	}

+ 28 - 0
Include/RmlUi/Core/DataView.h

@@ -118,6 +118,34 @@ private:
 	DataExpressionPtr data_expression;
 };
 
+class DataViewClass final : public DataView {
+public:
+	DataViewClass(DataModel& model, Element* element, const String& binding_name, const String& class_name);
+	~DataViewClass();
+
+	bool Update(DataModel& model) override;
+
+	StringList GetVariableNameList() const override;
+
+private:
+	String class_name;
+	DataExpressionPtr data_expression;
+};
+
+class DataViewRml final : public DataView {
+public:
+	DataViewRml(DataModel& model, Element* element, const String& binding_name, const String& rml_contents);
+	~DataViewRml();
+
+	bool Update(DataModel& model) override;
+
+	StringList GetVariableNameList() const override;
+
+private:
+	String previous_rml;
+	DataExpressionPtr data_expression;
+};
+
 
 class DataViewIf final : public DataView {
 public:

+ 6 - 2
Samples/basic/databinding/data/databinding.rml

@@ -87,6 +87,9 @@ p.title
 { 
 	cursor: pointer;
 }
+.red {
+	color: #e44;
+}
 
 
 /***  Decorators  ***/
@@ -167,13 +170,14 @@ form h2
 	<p>Data binding demo. We rate this a good old {{rating}}!</p>
 	<input type="range" name="rating" min="0" max="100" step="1" value="50" data-value="rating"/>
 	<div data-if="rating > 50">Thanks for the <span data-if="rating < 80">good</span><span data-if="rating >= 80">awesome</span> rating!</div>
+	<div data-rml="'Rml from the attribute? <strong>Yup!</strong>'"></div>
 	<h1>{{delightful_invader.name}}</h1>
-	<img data-attr-sprite="delightful_invader.sprite" data-style-image-color="delightful_invader.sprite == 'icon-invader' ? 'black' : 'green'"/>
+	<img data-attr-sprite="delightful_invader.sprite" data-style-image-color="rating < 80 ? 'black' : 'green'"/>
 	<p>
 		Indices: <span style="padding-left: 1em;" data-for="i : indices"> {{ i * 2 + (i > 10 ? ' wow!' | to_upper : '') }}</span>
 	</p>
 	<div data-for="invader : invaders">
-		<h1>{{invader.name}}</h1>
+		<h1 data-class-red="rating < 20">{{invader.name}}</h1>
 		<img data-attr-sprite="invader.sprite" data-style-image-color="invader.color"/>
 		<p>
 			Numbers: <span data-for="invader.numbers"> {{it}} </span>

+ 16 - 16
Source/Core/DataParser.cpp

@@ -103,7 +103,7 @@ namespace Parse {
 
 class DataParser {
 public:
-	DataParser(String expression, DataVariableInterface variable_interface) : expression(std::move(expression)), variable_interface(variable_interface) {}
+	DataParser(String expression, DataExpressionInterface expression_interface) : expression(std::move(expression)), expression_interface(expression_interface) {}
 
 	char Look() {
 		if (reached_end)
@@ -218,7 +218,7 @@ public:
 		program.push_back(InstructionData{ Instruction::Arguments, Variant(int(num_arguments)) });
 	}
 	void Variable(const String& name) {
-		DataAddress address = variable_interface.ParseAddress(name);
+		DataAddress address = expression_interface.ParseAddress(name);
 		if (address.empty()) {
 			Error(CreateString(name.size() + 50, "Could not find data variable with name '%s'.", name.c_str()));
 			return;
@@ -230,7 +230,7 @@ public:
 
 private:
 	const String expression;
-	DataVariableInterface variable_interface;
+	DataExpressionInterface expression_interface;
 
 	size_t index = 0;
 	bool reached_end = false;
@@ -669,7 +669,7 @@ static String DumpProgram(const Program& program)
 
 class DataInterpreter {
 public:
-	DataInterpreter(const Program& program, const AddressList& addresses, DataVariableInterface variable_interface) : program(program), addresses(addresses), variable_interface(variable_interface) {}
+	DataInterpreter(const Program& program, const AddressList& addresses, DataExpressionInterface expression_interface) : program(program), addresses(addresses), expression_interface(expression_interface) {}
 
 	bool Error(String message) const
 	{
@@ -716,7 +716,7 @@ private:
 
 	const Program& program;
 	const AddressList& addresses;
-	DataVariableInterface variable_interface;
+	DataExpressionInterface expression_interface;
 
 	bool Execute(const Instruction instruction, const Variant& data)
 	{
@@ -756,7 +756,7 @@ private:
 		{
 			size_t variable_index = size_t(data.Get<int>(-1));
 			if (variable_index < addresses.size())
-				R = variable_interface.GetValue(addresses[variable_index]);
+				R = expression_interface.GetValue(addresses[variable_index]);
 			else
 				return Error("Variable address not found.");
 		}
@@ -824,7 +824,7 @@ private:
 		{
 			const String function_name = data.Get<String>();
 			
-			if (!variable_interface.CallTransform(function_name, R, arguments))
+			if (!expression_interface.CallTransform(function_name, R, arguments))
 			{
 				String arguments_str;
 				for (size_t i = 0; i < arguments.size(); i++)
@@ -876,7 +876,7 @@ struct TestParser {
 
 	String TestExpression(String expression)
 	{
-		DataVariableInterface interface(&model, nullptr);
+		DataExpressionInterface interface(&model, nullptr);
 
 		DataParser parser(expression, interface);
 		if (parser.Parse())
@@ -907,7 +907,7 @@ DataExpression::~DataExpression()
 {
 }
 
-bool DataExpression::Parse(const DataVariableInterface& variable_interface)
+bool DataExpression::Parse(const DataExpressionInterface& expression_interface)
 {
 	// @todo: Remove, debugging only
 	static TestParser test_parser;
@@ -916,7 +916,7 @@ bool DataExpression::Parse(const DataVariableInterface& variable_interface)
 	//  3. Create a plug-in wrapper for use by scripting languages to replace this parser. Design wrapper as for events.
 	//  5. Add tests
 
-	DataParser parser(expression, variable_interface);
+	DataParser parser(expression, expression_interface);
 	if (!parser.Parse())
 		return false;
 
@@ -926,9 +926,9 @@ bool DataExpression::Parse(const DataVariableInterface& variable_interface)
 	return true;
 }
 
-bool DataExpression::Run(const DataVariableInterface& variable_interface, Variant& out_value)
+bool DataExpression::Run(const DataExpressionInterface& expression_interface, Variant& out_value)
 {
-	DataInterpreter interpreter(program, addresses, variable_interface);
+	DataInterpreter interpreter(program, addresses, expression_interface);
 	
 	if (!interpreter.Run())
 		return false;
@@ -949,20 +949,20 @@ StringList DataExpression::GetVariableNameList() const
 	return list;
 }
 
-DataVariableInterface::DataVariableInterface(DataModel* data_model, Element* element) : data_model(data_model), element(element)
+DataExpressionInterface::DataExpressionInterface(DataModel* data_model, Element* element) : data_model(data_model), element(element)
 {}
 
-DataAddress DataVariableInterface::ParseAddress(const String& address_str) const {
+DataAddress DataExpressionInterface::ParseAddress(const String& address_str) const {
 	return data_model ? data_model->ResolveAddress(address_str, element) : DataAddress();
 }
-Variant DataVariableInterface::GetValue(const DataAddress& address) const {
+Variant DataExpressionInterface::GetValue(const DataAddress& address) const {
 	Variant result;
 	if (data_model)
 		data_model->GetValue(address, result);
 	return result;
 }
 
-bool DataVariableInterface::CallTransform(const String& name, Variant& inout_variant, const VariantList& arguments)
+bool DataExpressionInterface::CallTransform(const String& name, Variant& inout_variant, const VariantList& arguments)
 {
 	return data_model ? data_model->CallTransform(name, inout_variant, arguments) : false;
 }

+ 5 - 5
Source/Core/DataParser.h

@@ -42,10 +42,10 @@ struct InstructionData;
 using Program = std::vector<InstructionData>;
 using AddressList = std::vector<DataAddress>;
 
-class DataVariableInterface {
+class DataExpressionInterface {
 public:
-    DataVariableInterface() = default;
-    DataVariableInterface(DataModel* data_model, Element* element);
+    DataExpressionInterface() = default;
+    DataExpressionInterface(DataModel* data_model, Element* element);
 
     DataAddress ParseAddress(const String& address_str) const;
     Variant GetValue(const DataAddress& address) const;
@@ -61,9 +61,9 @@ public:
     DataExpression(String expression);
     ~DataExpression();
 
-    bool Parse(const DataVariableInterface& variable_interface);
+    bool Parse(const DataExpressionInterface& expression_interface);
 
-    bool Run(const DataVariableInterface& variable_interface, Variant& out_value);
+    bool Run(const DataExpressionInterface& expression_interface, Variant& out_value);
 
     // Must be available after Parse()
     StringList GetVariableNameList() const;

+ 87 - 10
Source/Core/DataView.cpp

@@ -57,7 +57,7 @@ DataViewText::DataViewText(DataModel& model, ElementText* parent_element, const
 {
 	text.reserve(in_text.size());
 
-	DataVariableInterface variable_interface(&model, parent_element);
+	DataExpressionInterface expression_interface(&model, parent_element);
 	bool success = true;
 
 	size_t previous_close_brackets = 0;
@@ -79,7 +79,7 @@ DataViewText::DataViewText(DataModel& model, ElementText* parent_element, const
 		entry.index = text.size();
 		entry.data_expression = std::make_unique<DataExpression>(String(in_text.begin() + begin_name, in_text.begin() + end_name));
 
-		if (entry.data_expression->Parse(variable_interface))
+		if (entry.data_expression->Parse(expression_interface))
 			data_entries.push_back(std::move(entry));
 
 		previous_close_brackets = end_name + 2;
@@ -107,13 +107,13 @@ bool DataViewText::Update(DataModel& model)
 {
 	bool entries_modified = false;
 	Element* element = GetElement();
-	DataVariableInterface variable_interface(&model, element);
+	DataExpressionInterface expression_interface(&model, element);
 
 	for (DataEntry& entry : data_entries)
 	{
 		RMLUI_ASSERT(entry.data_expression);
 		Variant variant;
-		bool result = entry.data_expression->Run(variable_interface, variant);
+		bool result = entry.data_expression->Run(expression_interface, variant);
 		const String value = variant.Get<String>();
 		if (result && entry.value != value)
 		{
@@ -191,7 +191,7 @@ DataViewAttribute::DataViewAttribute(DataModel& model, Element* element, const S
 	: DataView(element), attribute_name(attribute_name)
 {
 	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataVariableInterface interface(&model, element);
+	DataExpressionInterface interface(&model, element);
 
 	if (!data_expression->Parse(interface))
 		InvalidateView();
@@ -204,7 +204,7 @@ bool DataViewAttribute::Update(DataModel& model)
 	bool result = false;
 	Variant variant;
 	Element* element = GetElement();
-	DataVariableInterface interface(&model, element);
+	DataExpressionInterface interface(&model, element);
 
 	if (element && data_expression->Run(interface, variant))
 	{
@@ -229,7 +229,7 @@ DataViewStyle::DataViewStyle(DataModel& model, Element* element, const String& b
 	: DataView(element), property_name(property_name)
 {
 	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataVariableInterface interface(&model, element);
+	DataExpressionInterface interface(&model, element);
 
 	if(!data_expression->Parse(interface))
 		InvalidateView();
@@ -245,7 +245,7 @@ bool DataViewStyle::Update(DataModel& model)
 	bool result = false;
 	Variant variant;
 	Element* element = GetElement();
-	DataVariableInterface interface(&model, element);
+	DataExpressionInterface interface(&model, element);
 	
 	if (element && data_expression->Run(interface, variant))
 	{
@@ -265,12 +265,89 @@ StringList DataViewStyle::GetVariableNameList() const {
 }
 
 
+DataViewClass::DataViewClass(DataModel& model, Element* element, const String& binding_name, const String& class_name) 
+	: DataView(element), class_name(class_name)
+{
+	data_expression = std::make_unique<DataExpression>(binding_name);
+	DataExpressionInterface interface(&model, element);
+
+	if (!data_expression->Parse(interface))
+		InvalidateView();
+}
+
+DataViewClass::~DataViewClass() = default;
+
+bool DataViewClass::Update(DataModel& model)
+{
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+
+	if (element && data_expression->Run(interface, variant))
+	{
+		const bool activate = variant.Get<bool>();
+		const bool is_set = element->IsClassSet(class_name);
+		if (activate != is_set)
+		{
+			element->SetClass(class_name, activate);
+			result = true;
+		}
+	}
+	return result;
+}
+
+StringList DataViewClass::GetVariableNameList() const
+{
+	return data_expression ? data_expression->GetVariableNameList() : StringList();
+}
+
+
+
+DataViewRml::DataViewRml(DataModel& model, Element* element, const String& binding_name, const String& /*unused*/)
+	: DataView(element)
+{
+	data_expression = std::make_unique<DataExpression>(binding_name);
+	DataExpressionInterface interface(&model, element);
+
+	if (!data_expression->Parse(interface))
+		InvalidateView();
+}
+
+DataViewRml::~DataViewRml() = default;
+
+bool DataViewRml::Update(DataModel & model)
+{
+	bool result = false;
+	Variant variant;
+	Element* element = GetElement();
+	DataExpressionInterface interface(&model, element);
+
+	if (element && data_expression->Run(interface, variant))
+	{
+		String new_rml = variant.Get<String>();
+		if (new_rml != previous_rml)
+		{
+			element->SetInnerRML(new_rml);
+			previous_rml = std::move(new_rml);
+			result = true;
+		}
+	}
+	return result;
+}
+
+StringList DataViewRml::GetVariableNameList() const
+{
+	return data_expression ? data_expression->GetVariableNameList() : StringList();
+}
+
+
 
 
 DataViewIf::DataViewIf(DataModel& model, Element* element, const String& binding_name) : DataView(element)
 {
 	data_expression = std::make_unique<DataExpression>(binding_name);
-	DataVariableInterface interface(&model, element);
+	DataExpressionInterface interface(&model, element);
 
 	if (!data_expression->Parse(interface))
 		InvalidateView();
@@ -285,7 +362,7 @@ bool DataViewIf::Update(DataModel& model)
 	bool result = false;
 	Variant variant;
 	Element* element = GetElement();
-	DataVariableInterface interface(&model, element);
+	DataExpressionInterface interface(&model, element);
 
 	if (element && data_expression->Run(interface, variant))
 	{

+ 27 - 8
Source/Core/ElementUtilities.cpp

@@ -397,16 +397,17 @@ void ElementUtilities::ApplyDataViewsControllers(Element* element)
 
 			if (name.size() > 5 && name[0] == 'd' && name[1] == 'a' && name[2] == 't' && name[3] == 'a' && name[4] == '-')
 			{
+				constexpr size_t data_str_size = sizeof("data");
 				const size_t data_type_end = name.find('-', 5);
 				const size_t count = (data_type_end == String::npos ? String::npos : data_type_end - 5);
 				const String data_type = name.substr(5, count);
-				const String value_bind_name = attribute.second.Get<String>();
+				const String data_expression = attribute.second.Get<String>();
 
 				if (data_type == "attr")
 				{
-					const String attr_bind_name = name.substr(5 + data_type.size() + 1);
+					const String attr_bind_name = name.substr(data_str_size + data_type.size() + 1);
 
-					auto view = std::make_unique<DataViewAttribute>(*data_model, element, value_bind_name, attr_bind_name);
+					auto view = std::make_unique<DataViewAttribute>(*data_model, element, data_expression, attr_bind_name);
 					if (*view)
 						data_model->AddView(std::move(view));
 					else
@@ -415,13 +416,13 @@ void ElementUtilities::ApplyDataViewsControllers(Element* element)
 				else if (data_type == "value")
 				{
 					const String attr_bind_name = "value";
-					auto view = std::make_unique<DataViewAttribute>(*data_model, element, value_bind_name, attr_bind_name);
+					auto view = std::make_unique<DataViewAttribute>(*data_model, element, data_expression, attr_bind_name);
 					if (*view)
 						data_model->AddView(std::move(view));
 					else
 						Log::Message(Log::LT_WARNING, "Could not add data-value view to element '%s'.", element->GetAddress().c_str());
 
-					auto controller = std::make_unique<DataControllerValue>(*data_model, element, value_bind_name);
+					auto controller = std::make_unique<DataControllerValue>(*data_model, element, data_expression);
 					if (controller)
 						data_model->AddController(std::move(controller));
 					else
@@ -429,17 +430,35 @@ void ElementUtilities::ApplyDataViewsControllers(Element* element)
 				}
 				else if (data_type == "style")
 				{
-					const String property_name = name.substr(5 + data_type.size() + 1);
+					const String property_name = name.substr(data_str_size + data_type.size() + 1);
 
-					auto view = std::make_unique<DataViewStyle>(*data_model, element, value_bind_name, property_name);
+					auto view = std::make_unique<DataViewStyle>(*data_model, element, data_expression, property_name);
 					if (*view)
 						data_model->AddView(std::move(view));
 					else
 						Log::Message(Log::LT_WARNING, "Could not add data-style view to element '%s'.", element->GetAddress().c_str());
 				}
+				else if (data_type == "class")
+				{
+					const String class_name = name.substr(data_str_size + data_type.size() + 1);
+
+					auto view = std::make_unique<DataViewClass>(*data_model, element, data_expression, class_name);
+					if (*view)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-class view to element '%s'.", element->GetAddress().c_str());
+				}
+				else if (data_type == "rml")
+				{
+					auto view = std::make_unique<DataViewRml>(*data_model, element, data_expression, String());
+					if (*view)
+						data_model->AddView(std::move(view));
+					else
+						Log::Message(Log::LT_WARNING, "Could not add data-rml view to element '%s'.", element->GetAddress().c_str());
+				}
 				else if (data_type == "if")
 				{
-					auto view = std::make_unique<DataViewIf>(*data_model, element, value_bind_name);
+					auto view = std::make_unique<DataViewIf>(*data_model, element, data_expression);
 					if (*view)
 						data_model->AddView(std::move(view));
 					else