diff --git a/scripts/properties/entity.gd b/scripts/properties/entity.gd index 131d0c8..dec9b3f 100644 --- a/scripts/properties/entity.gd +++ b/scripts/properties/entity.gd @@ -2,12 +2,129 @@ extends Reference class_name GDB_Entity +var _title := "" +var _title_property : Object = null var _properties := [].duplicate() var _property_ids := [].duplicate() +class _CommonWrapperProperty extends GDB_Property: + var object : WeakRef + func _init(object_ : Object, val, sig_name := "", flags := 0) -> void: + self.object = weakref(object_) + .set_type(typeof(val)) + .set_value(val) + .set_flags(flags) + + if sig_name != "": + object_.connect(sig_name, self, "_on_object_value_changed") + + func _on_object_value_changed(p0 = null, p1 = null, p2 = null, p3 = null) -> void: + var obj : Object = self.object.get_ref() + if obj: # shouldn't actually be null, who called the handler then? + _value = obj.get(self.member) + emit_signal("value_changed") + +class _WrapperProperty extends _CommonWrapperProperty: + var member : String + + func _init(object_ : Object, member_ : String, sig_name : String, flags : int) \ + .(object_, object_.get(member), sig_name, flags) -> void: + self.member = member_ + .set_name(member_.capitalize()) + + func get_value(): + var obj : Object = self.object.get_ref() + if obj: + return obj.get(self.member) + return .get_value() + + func set_value(value) -> void: + var obj : Object = self.object.get_ref() + if obj: + obj.set(self.member, value) + .set_value(obj.get(self.member)) + else: + .set_value(value) + +class _ArrayWrapperProperty extends _WrapperProperty: + func _init(object_ : Object, member_ : String, sig_name_added : String, sig_name_removed : String, flags : int) \ + .(object_, member_, "", flags) -> void: + if sig_name_added != "": + object_.connect(sig_name_added, self, "_on_object_value_added") + if sig_name_removed != "": + object_.connect(sig_name_added, self, "_on_object_value_removed") + + func _on_object_value_added(value, p0 = null, p1 = null, p2 = null, p3 = null) -> void: + var obj : Object = self.object.get_ref() + if obj: + _value = obj.get(self.member) + emit_signal("value_changed") + emit_signal("value_added", value) + + func _on_object_value_removed(value, p0 = null, p1 = null, p2 = null, p3 = null) -> void: + var obj : Object = self.object.get_ref() + if obj: + _value = obj.get(self.member) + emit_signal("value_changed") + emit_signal("value_removed", value) + +class _GetSetWrapperProperty extends _CommonWrapperProperty: + var getter := "" + var setter := "" + + func _init(object_ : Object, getter_ : String, setter_ := "", sig_name := "", flags := 0) \ + .(object_, object.call(getter_), sig_name, flags) -> void: + self.getter = getter_ + self.setter = setter_ + + func get_value(): + var obj : Object = self.object.get_ref() + if obj: + return obj.call(self.getter) + return .get_value() + + func set_value(value) -> void: + if !self.setter: + return + var obj : Object = self.object.get_ref() + if obj: + obj.call(self.setter, value) + .set_value(obj.call(self.getter)) + else: + .set_value(value) + ################ # public stuff # ################ +func get_title() -> String: + return _title + +func set_title(title : String) -> void: + if _title_property: + # title and title_property are mutually exclusive + GDBUtility.disconnect_all(_title_property, self) + _title_property = null + + __set_title(title) + +func set_title_property(property : Object) -> void: + if property.get_type() != TYPE_STRING: + printerr("Title property must be string.") + return + + if _title_property == property: + return + + if _title_property: + GDBUtility.disconnect_all(_title_property, self) + + _title_property = property + var new_title := "" + if _title_property: + new_title = _title_property.get_value() + GDBUtility.try_connect(_title_property, "value_changed", self, "_on_title_property_value_changed") + __set_title(new_title) + func get_properties() -> Array: return _properties @@ -67,6 +184,14 @@ func find_property(property : Object) -> int: func find_property_by_id(id : String) -> int: return _property_ids.find(id) +################# +# private stuff # +################# +func __set_title(title : String) -> void: + if title != _title: + _title = title + emit_signal("entity_title_changed") + ################ # static stuff # ################ @@ -74,8 +199,62 @@ static func is_valid_entity(object : Object) -> bool: return object != null \ && object.has_method("get_properties") +static func make_property(object : Object, member : String, sig_name := "", flags := 0) -> GDB_Property: + return _WrapperProperty.new(object, member, sig_name, flags) + +static func make_getset_property(object : Object, getter : String, setter := "", sig_name := "", flags := 0) -> GDB_Property: + return _GetSetWrapperProperty.new(object, getter, setter, sig_name, flags) + +static func make_array_property(object : Object, member : String, sig_name_added := "", sig_name_removed := "", array_type := TYPE_NIL, flags := 0) -> GDB_Property: + var prop := _ArrayWrapperProperty.new(object, member, sig_name_added, sig_name_removed, flags) + prop.set_content_type(array_type) + return prop + +static func make_entity_title_property(entity : Object) -> Object: + var property : Object + if entity.has_method("get_title_property"): + property = entity.get_title_property() + if property: + return property + if entity.get("title") is String: + var sig_name := "" + if entity.has_signal("title_changed"): + sig_name = "title_changed" + return make_property(entity, "title", sig_name) + if entity.has_method("get_title"): + var setter := "" + var sig_name := "" + var flags := 0 + if entity.has_method("set_title"): + setter = "set_title" + else: + flags |= GDB_Property.FLAG_READONLY + if entity.has_signal("title_changed"): + sig_name = "title_changed" + return make_getset_property(entity, "get_title", setter, sig_name, flags) + return null + + +static func get_entity_property_ids(entity : Object) -> Array: + return __get_entity_optional(entity, "property_ids", []) + +################# +# private stuff # +################# +static func __get_entity_optional(entity : Object, name : String, default = null): + return GDB_Property.__get_prop_optional(entity, name, default) # why write this twice? + +############ +# handlers # +############ +func _on_title_property_value_changed() -> void: + if !_title_property: # should exist + return + __set_title(_title_property.get_value()) + ########### # signals # ########### +signal entity_title_changed() signal property_added(index) signal property_removed(index, property) diff --git a/scripts/properties/property.gd b/scripts/properties/property.gd index 9b7f515..cb1afd5 100644 --- a/scripts/properties/property.gd +++ b/scripts/properties/property.gd @@ -3,12 +3,19 @@ extends Reference class_name GDB_Property # special types -const TYPE_HEADER = -1 +const TYPE_HEADER = -1 +const TYPE_ENTITY = -2 +const TYPE_PROPERTY = -3 + +const FLAG_READONLY = (1 << 0) # the user should not be able to change this +const FLAG_HIDDEN = (1 << 1) # completely hidden from UI var _name := "" var _icon : Texture = null var _value = null -var _type := TYPE_INT +var _type := TYPE_INT# +var _content_type := TYPE_NIL +var _flags := 0 ################ # overridables # @@ -43,7 +50,23 @@ func get_type() -> int: func set_type(type : int) -> void: if type != _type: _type = type - emit_signal("type_changed") + emit_signal("type_changed") + +func get_content_type() -> int: + return _content_type + +func set_content_type(type : int) -> void: + if type != _content_type: + _content_type = type + emit_signal("type_changed") + +func get_flags() -> int: + return _flags + +func set_flags(flags : int) -> void: + if flags != _flags: + _flags = flags + emit_signal("flags_changed") ################ # static stuff # @@ -55,12 +78,13 @@ static func is_valid_property(object : Object) -> bool: && object.has_method("get_type") static func get_prop_icon(property : Object) -> Texture: - var icon : Texture = null - if property.has_method("get_icon"): - icon = property.get_icon() as Texture - else: - icon = property.get("icon") as Texture - return icon + return __get_prop_optional(property, "icon") + +static func get_prop_flags(property : Object) -> int: + return __get_prop_optional(property, "flags", 0) + +static func get_prop_content_type(property : Object) -> int: + return __get_prop_optional(property, "content_type", TYPE_NIL) static func is_prop_value_valid(property : Object, value) -> bool: if property.has_method("is_value_valud"): @@ -69,12 +93,16 @@ static func is_prop_value_valid(property : Object, value) -> bool: match property.get_type(): TYPE_HEADER: return false + TYPE_BOOL: + return value is bool TYPE_INT: return value is int TYPE_REAL: return value is float TYPE_STRING: return value is String + TYPE_ARRAY: + return value is Array _: # TODO: more default checks return true @@ -83,6 +111,16 @@ static func get_prop_value_as_string(property : Object) -> String: return property.get_value_as_string() return str(property.get_value()) +static func __get_prop_optional(property : Object, name : String, default = null): + var value = default + if property.has_method("get_" + name): + value = property.call("get_" + name) + else: + var val = property.get(name) + if typeof(default) == TYPE_NIL || typeof(val) == typeof(default): + value = val + return value + ########### # signals # ########### @@ -90,3 +128,6 @@ signal name_changed() signal icon_changed() signal value_changed() signal type_changed() +signal flags_changed() +signal value_added(value) +signal value_removed(value)