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 @@ + + + + +0,0, +0,0 + + + + + + + + + + + + + + + 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);