From f9d7cfdc68088f832d28e12ba56b66cad6899699 Mon Sep 17 00:00:00 2001 From: HJfod <60038575+HJfod@users.noreply.github.com> Date: Thu, 22 Feb 2024 23:39:21 +0200 Subject: [PATCH] replace attributes with an augmented user object system --- .../include/Geode/cocos/base_nodes/CCNode.h | 62 +++++++------------ loader/src/hooks/GeodeNodeMetadata.cpp | 52 +++++++++------- loader/src/ui/nodes/InputNode.cpp | 2 +- loader/src/ui/nodes/TextInput.cpp | 4 +- 4 files changed, 55 insertions(+), 65 deletions(-) diff --git a/loader/include/Geode/cocos/base_nodes/CCNode.h b/loader/include/Geode/cocos/base_nodes/CCNode.h index cee25443..789ea67c 100644 --- a/loader/include/Geode/cocos/base_nodes/CCNode.h +++ b/loader/include/Geode/cocos/base_nodes/CCNode.h @@ -837,10 +837,25 @@ public: * The UserObject will be retained once in this method, * and the previous UserObject (if existed) will be relese. * The UserObject will be released in CCNode's destructure. + * + * @note In Geode, this actually sets the user object with the ID "" + * (empty string) * * @param A user assigned CCObject */ virtual void setUserObject(CCObject *pUserObject); + + /** + * Set a user-assigned CCObject with a specific ID. This allows nodes to + * have multiple user objects. Objects should be prefixed with the mod ID. + * Assigning a null removes the user object with the ID + */ + GEODE_DLL void setUserObject(std::string const& id, CCObject* object); + + /** + * Get a user-assigned CCObject with the specific ID + */ + GEODE_DLL CCObject* getUserObject(std::string const& id); /// @} end of Tag & User Data @@ -848,9 +863,6 @@ private: friend class geode::modifier::FieldContainer; GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(); -#ifndef GEODE_IS_MEMBER_TEST - GEODE_DLL std::optional getAttributeInternal(std::string const& attribute); -#endif GEODE_DLL void addEventListenerInternal( std::string const& id, geode::EventListenerProtocol* protocol @@ -928,38 +940,6 @@ public: */ GEODE_DLL bool hasAncestor(CCNode* ancestor); -#ifndef GEODE_IS_MEMBER_TEST - /** - * Set an attribute on a node. Attributes are a system added by Geode, - * where a node may have any sort of extra data associated with it. Used - * for mod intercommunication. For example, a mod that adds scrollbars to - * layers might check if the layer has an attribute set for whether the - * scrollbar should be disabled. The key of the attribute should be - * prefixed with the mod ID, like hjfod.cool-scrollbars/enable. - * @param attribute The attribute key. Should be prefixed with the mod ID, - * like hjfod.cool-scrollbars/enable - * @param value The value of the attribute - * @note Geode addition - */ - GEODE_DLL void setAttribute(std::string const& attribute, matjson::Value const& value); - /** - * Get an attribute from the node. Attributes may be anything - * @param attribute The attribute key - * @returns The value, or nullopt if the attribute doesn't exist or if the - * type didn't match - * @note Geode addition - */ - template - std::optional getAttribute(std::string const& attribute) { - if (auto value = this->getAttributeInternal(attribute)) { - if (value.value().template is()) { - return value.value().template as(); - } - } - return std::nullopt; - } -#endif - /** * Set the Layout for this node. Used to automatically position children, * based on the selected layout. In order to apply the layout after a child @@ -1834,23 +1814,23 @@ NS_CC_END #ifndef GEODE_IS_MEMBER_TEST namespace geode { - struct GEODE_DLL AttributeSetEvent : public Event { + struct GEODE_DLL UserObjectSetEvent : public Event { cocos2d::CCNode* node; const std::string id; - matjson::Value& value; + cocos2d::CCObject* value; - AttributeSetEvent(cocos2d::CCNode* node, std::string const& id, matjson::Value& value); + UserObjectSetEvent(cocos2d::CCNode* node, std::string const& id, cocos2d::CCObject* value); }; - class GEODE_DLL AttributeSetFilter : public EventFilter { + class GEODE_DLL AttributeSetFilter : public EventFilter { public: - using Callback = void(AttributeSetEvent*); + using Callback = void(UserObjectSetEvent*); protected: std::string m_targetID; public: - ListenerResult handle(utils::MiniFunction fn, AttributeSetEvent* event); + ListenerResult handle(utils::MiniFunction fn, UserObjectSetEvent* event); AttributeSetFilter(std::string const& id); }; diff --git a/loader/src/hooks/GeodeNodeMetadata.cpp b/loader/src/hooks/GeodeNodeMetadata.cpp index 14f44289..389ba442 100644 --- a/loader/src/hooks/GeodeNodeMetadata.cpp +++ b/loader/src/hooks/GeodeNodeMetadata.cpp @@ -17,11 +17,10 @@ struct ProxyCCNode; class GeodeNodeMetadata final : public cocos2d::CCObject { private: FieldContainer* m_fieldContainer; - Ref m_userObject; std::string m_id = ""; Ref m_layout = nullptr; Ref m_layoutOptions = nullptr; - std::unordered_map m_attributes; + std::unordered_map> m_userObjects; std::unordered_set> m_eventListeners; std::unordered_map> m_idEventListeners; @@ -53,8 +52,8 @@ public: meta->retain(); if (old) { - meta->m_userObject = old; - // the old user object has been retained by CCNode + meta->m_userObjects.insert({ "", old }); + // the old user object is now managed by Ref old->release(); } return meta; @@ -69,18 +68,24 @@ public: #include struct ProxyCCNode : Modify { virtual CCObject* getUserObject() { - if (typeinfo_cast(this)) { - return GeodeNodeMetadata::set(this)->m_userObject; + if (auto asNode = typeinfo_cast(this)) { + return asNode->getUserObject(""); + } + else { + // apparently this function is the same as + // CCDirector::getNextScene so yeah + return m_pUserObject; } - // apparently this function is the same as - // CCDirector::getNextScene so yeah - return m_pUserObject; } virtual void setUserObject(CCObject* obj) { - if (typeinfo_cast(this)) { - GeodeNodeMetadata::set(this)->m_userObject = obj; + if (auto asNode = typeinfo_cast(this)) { + asNode->setUserObject("", obj); + } + else { + CC_SAFE_RELEASE(m_pUserObject); + m_pUserObject = obj; + CC_SAFE_RETAIN(m_pUserObject); } - m_pUserObject = obj; } }; @@ -166,10 +171,10 @@ void CCNode::updateLayout(bool updateChildOrder) { } } -AttributeSetEvent::AttributeSetEvent(CCNode* node, std::string const& id, matjson::Value& value) +UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value) : node(node), id(id), value(value) {} -ListenerResult AttributeSetFilter::handle(MiniFunction fn, AttributeSetEvent* event) { +ListenerResult AttributeSetFilter::handle(MiniFunction fn, UserObjectSetEvent* event) { if (event->id == m_targetID) { fn(event); } @@ -178,18 +183,23 @@ ListenerResult AttributeSetFilter::handle(MiniFunction fn, AttributeSe AttributeSetFilter::AttributeSetFilter(std::string const& id) : m_targetID(id) {} -void CCNode::setAttribute(std::string const& attr, matjson::Value const& value) { +void CCNode::setUserObject(std::string const& id, CCObject* value) { auto meta = GeodeNodeMetadata::set(this); - meta->m_attributes[attr] = value; - AttributeSetEvent(this, attr, meta->m_attributes.at(attr)).post(); + if (value) { + meta->m_userObjects[id] = value; + } + else { + meta->m_userObjects.erase(id); + } + UserObjectSetEvent(this, id, value).post(); } -std::optional CCNode::getAttributeInternal(std::string const& attr) { +CCObject* CCNode::getUserObject(std::string const& id) { auto meta = GeodeNodeMetadata::set(this); - if (meta->m_attributes.count(attr)) { - return meta->m_attributes.at(attr); + if (meta->m_userObjects.count(id)) { + return meta->m_userObjects.at(id); } - return std::nullopt; + return nullptr; } void CCNode::addEventListenerInternal(std::string const& id, EventListenerProtocol* listener) { diff --git a/loader/src/ui/nodes/InputNode.cpp b/loader/src/ui/nodes/InputNode.cpp index eb809ec6..e73059ec 100644 --- a/loader/src/ui/nodes/InputNode.cpp +++ b/loader/src/ui/nodes/InputNode.cpp @@ -56,7 +56,7 @@ bool InputNode::init( m_input->setMaxLabelScale(.85f); m_input->setMaxLabelLength(maxCharCount); m_input->setPosition(width / 2, height / 2); - m_input->setAttribute("fix-text-input", true); + m_input->setUserObject("fix-text-input", CCBool::create(true)); if (filter.length()) { m_input->setAllowedChars(filter); } diff --git a/loader/src/ui/nodes/TextInput.cpp b/loader/src/ui/nodes/TextInput.cpp index 7672a047..dbd56b4b 100644 --- a/loader/src/ui/nodes/TextInput.cpp +++ b/loader/src/ui/nodes/TextInput.cpp @@ -9,7 +9,7 @@ struct TextInputNodeFix : Modify { GEODE_FORWARD_COMPAT_DISABLE_HOOKS("TextInputNode fix") bool ccTouchBegan(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) { - if (!this->template getAttribute("fix-text-input").value_or(false)) { + if (!this->getUserObject("fix-text-input")) { return CCTextInputNode::ccTouchBegan(touch, event); } @@ -73,7 +73,7 @@ bool TextInput::init(float width, std::string const& placeholder, std::string co m_input->setLabelPlaceholderColor({ 150, 150, 150 }); m_input->setLabelPlaceholderScale(.6f); m_input->setMaxLabelScale(.6f); - m_input->setAttribute("fix-text-input", true); + m_input->setUserObject("fix-text-input", CCBool::create(true)); this->addChildAtPosition(m_input, cocos2d::Anchor::Center); return true;