diff --git a/CHANGELOG.md b/CHANGELOG.md
index f7a0f0f5..5203aa22 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add `text`, `width` and `height` members to `ObjectShape::Text`. (#278)
- Implement `ResourceReader` for appropiate functions. (#272) **Read the README's FAQ for more information about this change.**
+- Support for custom type properties. (#283)
## Fixed
- `ObjectShape::Text::kerning`'s default value, which should have been set to `true` instead of `false`. (#278)
diff --git a/assets/tiled_class_property.tmx b/assets/tiled_class_property.tmx
new file mode 100644
index 00000000..000ff531
--- /dev/null
+++ b/assets/tiled_class_property.tmx
@@ -0,0 +1,21 @@
+
+
diff --git a/src/properties.rs b/src/properties.rs
index 27ab4386..fb2b0334 100644
--- a/src/properties.rs
+++ b/src/properties.rs
@@ -82,6 +82,14 @@ pub enum PropertyValue {
/// An object ID value. Corresponds to the `object` property type.
/// Holds the id of a referenced object, or 0 if unset.
ObjectValue(u32),
+ /// A class value. Corresponds to the `class` property type.
+ /// Holds the type name and a set of properties.
+ ClassValue {
+ /// The type name.
+ property_type: String,
+ /// A set of properties.
+ properties: Properties,
+ },
}
impl PropertyValue {
@@ -135,15 +143,31 @@ pub(crate) fn parse_properties(
let mut p = HashMap::new();
parse_tag!(parser, "properties", {
"property" => |attrs:Vec| {
- let (t, v_attr, k) = get_attrs!(
+ let (t, v_attr, k, p_t) = get_attrs!(
for attr in attrs {
Some("type") => obj_type = attr,
Some("value") => value = attr,
+ Some("propertytype") => propertytype = attr,
"name" => name = attr
}
- (obj_type, value, name)
+ (obj_type, value, name, propertytype)
);
let t = t.unwrap_or_else(|| "string".to_owned());
+ if t == "class" {
+ // Class properties will have their member values stored in a nested
+ // element. Only the actually set members are saved. When no members have been set
+ // the properties element is left out entirely.
+ let properties = if has_properties_tag_next(parser) {
+ parse_properties(parser)?
+ } else {
+ HashMap::new()
+ };
+ p.insert(k, PropertyValue::ClassValue {
+ property_type: p_t.unwrap_or_default(),
+ properties,
+ });
+ return Ok(());
+ }
let v: String = match v_attr {
Some(val) => val,
@@ -164,3 +188,26 @@ pub(crate) fn parse_properties(
});
Ok(p)
}
+
+/// Checks if there is a properties tag next in the parser. Will consume any whitespace or comments.
+fn has_properties_tag_next(parser: &mut impl Iterator- ) -> bool {
+ let mut peekable = parser.by_ref().peekable();
+ while let Some(Ok(next)) = peekable.peek() {
+ match next {
+ XmlEvent::StartElement { name, .. } if name.local_name == "properties" => return true,
+ // Ignore whitespace and comments
+ XmlEvent::Whitespace(_) => {
+ peekable.next();
+ }
+ XmlEvent::Comment(_) => {
+ peekable.next();
+ }
+ // If we encounter anything else than whitespace, comments or the properties tag, we
+ // assume there are no properties.
+ _ => {
+ return false;
+ }
+ }
+ }
+ false
+}
diff --git a/tests/lib.rs b/tests/lib.rs
index c8c4a258..753326fb 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -220,6 +220,7 @@ fn test_object_group_property() {
};
assert!(prop_value);
}
+
#[test]
fn test_tileset_property() {
let r = Loader::new()
@@ -325,6 +326,33 @@ fn test_object_property() {
assert_eq!(3, prop_value);
}
+#[test]
+fn test_class_property() {
+ let r = Loader::new()
+ .load_tmx_map("assets/tiled_class_property.tmx")
+ .unwrap();
+ let layer = r.get_layer(1).unwrap();
+ if let Some(PropertyValue::ClassValue {
+ property_type,
+ properties,
+ }) = layer
+ .as_object_layer()
+ .unwrap()
+ .get_object(0)
+ .unwrap()
+ .properties
+ .get("class property")
+ {
+ assert_eq!(property_type, "test_type");
+ assert_eq!(
+ properties.get("test_property_1").unwrap(),
+ &PropertyValue::IntValue(3)
+ );
+ } else {
+ panic!("Expected class property");
+ };
+}
+
#[test]
fn test_tint_color() {
let r = Loader::new()
@@ -336,7 +364,7 @@ fn test_tint_color() {
alpha: 0x12,
red: 0x34,
green: 0x56,
- blue: 0x78
+ blue: 0x78,
})
);
assert_eq!(
@@ -345,7 +373,7 @@ fn test_tint_color() {
alpha: 0xFF,
red: 0x12,
green: 0x34,
- blue: 0x56
+ blue: 0x56,
})
);
}
@@ -370,7 +398,7 @@ fn test_group_layers() {
alpha: 0x12,
red: 0x34,
green: 0x56,
- blue: 0x78
+ blue: 0x78,
})),
layer_group_1.properties.get("key")
);
@@ -417,7 +445,7 @@ fn test_object_template_property() {
object.shape,
ObjectShape::Rect {
width: 32.0,
- height: 32.0
+ height: 32.0,
}
);
assert_eq!(object.x, 32.0);