|
@@ -8,7 +8,8 @@ namespace Spry {
|
|
|
public errordomain ComponentError {
|
|
public errordomain ComponentError {
|
|
|
INVALID_TYPE,
|
|
INVALID_TYPE,
|
|
|
ELEMENT_NOT_FOUND,
|
|
ELEMENT_NOT_FOUND,
|
|
|
- TYPE_NOT_FOUND;
|
|
|
|
|
|
|
+ TYPE_NOT_FOUND,
|
|
|
|
|
+ PROPERTY_NOT_FOUND;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public abstract class Component : Object, Renderable {
|
|
public abstract class Component : Object, Renderable {
|
|
@@ -198,9 +199,10 @@ namespace Spry {
|
|
|
|
|
|
|
|
private async void transform_document(MarkupDocument doc) throws Error {
|
|
private async void transform_document(MarkupDocument doc) throws Error {
|
|
|
remove_hidden_blocks(doc);
|
|
remove_hidden_blocks(doc);
|
|
|
|
|
+ yield transform_per_attributes(doc);
|
|
|
|
|
+ yield transform_property_attributes(doc);
|
|
|
yield transform_outlets(doc);
|
|
yield transform_outlets(doc);
|
|
|
yield transform_components(doc);
|
|
yield transform_components(doc);
|
|
|
- replace_control_blocks(doc);
|
|
|
|
|
transform_action_nodes(doc);
|
|
transform_action_nodes(doc);
|
|
|
transform_target_nodes(doc);
|
|
transform_target_nodes(doc);
|
|
|
transform_global_nodes(doc);
|
|
transform_global_nodes(doc);
|
|
@@ -227,11 +229,6 @@ namespace Spry {
|
|
|
.iterate(n => n.remove());
|
|
.iterate(n => n.remove());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private void replace_control_blocks(MarkupDocument doc) {
|
|
|
|
|
- doc.select("//spry-control")
|
|
|
|
|
- .iterate(n => n.replace_with_nodes(n.children));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
private void transform_action_nodes(MarkupDocument doc) throws Error {
|
|
private void transform_action_nodes(MarkupDocument doc) throws Error {
|
|
|
var action_nodes = doc.select("//*[@spry-action]");
|
|
var action_nodes = doc.select("//*[@spry-action]");
|
|
|
foreach(var node in action_nodes) {
|
|
foreach(var node in action_nodes) {
|
|
@@ -312,6 +309,212 @@ namespace Spry {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private void transform_if_attributes(MarkupDocument doc) throws Error {
|
|
|
|
|
+ var nodes = doc.select("//*[@spry-if]");
|
|
|
|
|
+ foreach (var node in nodes) {
|
|
|
|
|
+ var expression = node.get_attribute("spry-per");
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async void transform_per_attributes(MarkupDocument doc) throws Error {
|
|
|
|
|
+ var nodes = doc.select("//*[@spry-per]");
|
|
|
|
|
+ foreach (var node in nodes) {
|
|
|
|
|
+ var prop_path = node.get_attribute("spry-per");
|
|
|
|
|
+ var prop_value = get_ultimate_value(prop_path);
|
|
|
|
|
+
|
|
|
|
|
+ if(!prop_value.type().is_a(typeof(Enumerable))) {
|
|
|
|
|
+ throw new ComponentError.INVALID_TYPE(@"The spry-per attribute must refer to a value of type Invercargill.Enumerable, $prop_path is a $(prop_value.type().name())");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ node.remove_attribute("spry-per");
|
|
|
|
|
+ var values = ((Enumerable)prop_value).as_values();
|
|
|
|
|
+ var output_nodes = new Series<MarkupNode>();
|
|
|
|
|
+ foreach(var value in values) {
|
|
|
|
|
+ var fragment = new MarkupDocument();
|
|
|
|
|
+ fragment.body.append_node(node);
|
|
|
|
|
+ yield transform_property_attributes(fragment, value);
|
|
|
|
|
+ output_nodes.add_all(fragment.body.children);
|
|
|
|
|
+ }
|
|
|
|
|
+ node.replace_with_nodes(output_nodes);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private async void transform_property_attributes(MarkupDocument doc, Value? context = null) throws Error {
|
|
|
|
|
+ var nodes = doc.select("//*[@spry-prop]");
|
|
|
|
|
+ foreach (var node in nodes) {
|
|
|
|
|
+ var attr_string = node.get_attribute("spry-prop");
|
|
|
|
|
+ var paths = attr_string.split(";");
|
|
|
|
|
+ foreach (var prop_path in paths) {
|
|
|
|
|
+ string attribute = null;
|
|
|
|
|
+ string formatter = null;
|
|
|
|
|
+ if(prop_path.contains("=")) {
|
|
|
|
|
+ var parts = prop_path.split("=", 2);
|
|
|
|
|
+ attribute = parts[0].chomp().chug();
|
|
|
|
|
+ prop_path = parts[1].chomp().chug();
|
|
|
|
|
+ }
|
|
|
|
|
+ if(prop_path.contains(":")) {
|
|
|
|
|
+ var parts = prop_path.split(":", 2);
|
|
|
|
|
+ prop_path = parts[0].chomp().chug();
|
|
|
|
|
+ formatter = parts[1].chomp().chug();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Resolve the property path
|
|
|
|
|
+ var value = get_ultimate_value(prop_path, context);
|
|
|
|
|
+ var type = value.type();
|
|
|
|
|
+
|
|
|
|
|
+ // Render renderables
|
|
|
|
|
+ if(type.is_a(typeof(Renderable))) {
|
|
|
|
|
+ var renderable = (Renderable)value.get_object();
|
|
|
|
|
+ if(renderable != null) {
|
|
|
|
|
+ var document = yield renderable.to_document();
|
|
|
|
|
+ node.replace_with_nodes(document.body.children);
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ node.remove();
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(attribute.has_prefix("class.")) {
|
|
|
|
|
+ var class_name = attribute[6:];
|
|
|
|
|
+ if(!value.type().is_a(typeof(bool))) {
|
|
|
|
|
+ throw new ComponentError.INVALID_TYPE(@"Property \'$prop_path\' is not assignable to \'$attribute\', only bools are valid");
|
|
|
|
|
+ }
|
|
|
|
|
+ if(value.get_boolean()) {
|
|
|
|
|
+ node.add_class(class_name);
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ node.remove_class(class_name);
|
|
|
|
|
+ }
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Set text for stringified properties
|
|
|
|
|
+ var output = get_string_from_value(value, formatter);
|
|
|
|
|
+ if(attribute != null) {
|
|
|
|
|
+ if(attribute.has_prefix("style.")) {
|
|
|
|
|
+ node.set_style(attribute[6:], output);
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ node.set_attribute(attribute, output);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ node.text_content = output;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ node.remove_attribute("spry-prop");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Value get_ultimate_value(string property_path, Value? context = null) throws Error {
|
|
|
|
|
+ var path = property_path.split(".");
|
|
|
|
|
+ var value = context;
|
|
|
|
|
+
|
|
|
|
|
+ if(context == null || path[0] == "this") {
|
|
|
|
|
+ value = Value(get_type());
|
|
|
|
|
+ value.set_object(this);
|
|
|
|
|
+ if(path[0] == "this") {
|
|
|
|
|
+ path = path[1:];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else if(path[0] == "value") {
|
|
|
|
|
+ if(value.type().is_a(typeof(Object)) && value.get_object() == this) {
|
|
|
|
|
+ throw new ComponentError.PROPERTY_NOT_FOUND("Identifier 'value' not valid in this context");
|
|
|
|
|
+ }
|
|
|
|
|
+ path = path[1:];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ foreach(var prop_name in path) {
|
|
|
|
|
+ if(!value.type().is_a(typeof(Object))) {
|
|
|
|
|
+ throw new ComponentError.PROPERTY_NOT_FOUND(@"Cannot navigate to property \"$prop_name\" on type $(value.type_name()): Only Object types are navigable");
|
|
|
|
|
+ }
|
|
|
|
|
+ var source = value.get_object();
|
|
|
|
|
+ var prop_spec = source.get_class().find_property(prop_name);
|
|
|
|
|
+ if(prop_spec == null) {
|
|
|
|
|
+ throw new ComponentError.PROPERTY_NOT_FOUND(@"Could not find property \"$prop_name\" on object $(source.get_type().name())");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var type = prop_spec.value_type;
|
|
|
|
|
+ value = Value(type);
|
|
|
|
|
+ source.get_property(prop_name, ref value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private string get_string_from_value(Value value, string? formatter) {
|
|
|
|
|
+ var type = value.type();
|
|
|
|
|
+
|
|
|
|
|
+ if (type == typeof(bool)) {
|
|
|
|
|
+ return value.get_boolean() ? "true" : "false";
|
|
|
|
|
+ } else if (type == typeof(int)) {
|
|
|
|
|
+ var int_val = value.get_int();
|
|
|
|
|
+ return formatter != null ? int_val.to_string(formatter) : int_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(uint)) {
|
|
|
|
|
+ var uint_val = value.get_uint();
|
|
|
|
|
+ return formatter != null ? uint_val.to_string(formatter) : uint_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(long)) {
|
|
|
|
|
+ var long_val = value.get_long();
|
|
|
|
|
+ return formatter != null ? long_val.to_string(formatter) : long_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(ulong)) {
|
|
|
|
|
+ var ulong_val = value.get_ulong();
|
|
|
|
|
+ return formatter != null ? ulong_val.to_string(formatter) : ulong_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(int64)) {
|
|
|
|
|
+ var int64_val = value.get_int64();
|
|
|
|
|
+ return formatter != null ? int64_val.to_string(formatter) : int64_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(uint64)) {
|
|
|
|
|
+ var uint64_val = value.get_uint64();
|
|
|
|
|
+ return formatter != null ? uint64_val.to_string(formatter) : uint64_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(float)) {
|
|
|
|
|
+ var float_val = value.get_float();
|
|
|
|
|
+ return formatter != null ? float_val.to_string(formatter) : float_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(double)) {
|
|
|
|
|
+ var double_val = value.get_double();
|
|
|
|
|
+ return formatter != null ? @"%$(formatter)".printf(double_val) : double_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(string)) {
|
|
|
|
|
+ return value.get_string() ?? "";
|
|
|
|
|
+ } else if (type == typeof(char)) {
|
|
|
|
|
+ var char_val = value.get_schar();
|
|
|
|
|
+ return formatter != null ? char_val.to_string(formatter) : char_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(uchar)) {
|
|
|
|
|
+ var uchar_val = value.get_uchar();
|
|
|
|
|
+ return formatter != null ? uchar_val.to_string(formatter) : uchar_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(int8)) {
|
|
|
|
|
+ var int8_val = value.get_schar();
|
|
|
|
|
+ return formatter != null ? int8_val.to_string(formatter) : int8_val.to_string();
|
|
|
|
|
+ } else if (type.is_enum()) {
|
|
|
|
|
+ var enum_val = value.get_enum();
|
|
|
|
|
+ return formatter != null ? enum_val.to_string(formatter) : enum_val.to_string();
|
|
|
|
|
+ } else if (type.is_flags()) {
|
|
|
|
|
+ var flags_val = value.get_flags();
|
|
|
|
|
+ return formatter != null ? flags_val.to_string(formatter) : flags_val.to_string();
|
|
|
|
|
+ } else if (type == typeof(Type)) {
|
|
|
|
|
+ return value.get_gtype().name();
|
|
|
|
|
+ } else if (type.is_object()) {
|
|
|
|
|
+ var obj = value.get_object();
|
|
|
|
|
+ if (obj == null) {
|
|
|
|
|
+ return "null";
|
|
|
|
|
+ }
|
|
|
|
|
+ return @"<$(type.name())>";
|
|
|
|
|
+ } else if (type == typeof(Variant)) {
|
|
|
|
|
+ var variant = value.get_variant();
|
|
|
|
|
+ return variant != null ? variant.print(true) : "null";
|
|
|
|
|
+ } else if (type == typeof(ParamSpec)) {
|
|
|
|
|
+ var param = value.get_param();
|
|
|
|
|
+ return param != null ? param.name : "null";
|
|
|
|
|
+ } else if (type.is_derived() && !type.is_object() && !type.is_interface()) {
|
|
|
|
|
+ // Handle boxed types - derived but not object or interface
|
|
|
|
|
+ return @"<boxed $(type.name())>";
|
|
|
|
|
+ } else if (type == typeof(void*)) {
|
|
|
|
|
+ return @"<pointer 0x$(((int)value).to_string("%x"))>";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|