replace attributes with an augmented user object system

This commit is contained in:
HJfod 2024-02-22 23:39:21 +02:00
parent e452f482a8
commit f9d7cfdc68
4 changed files with 55 additions and 65 deletions

View file

@ -838,19 +838,31 @@ public:
* and the previous UserObject (if existed) will be relese. * and the previous UserObject (if existed) will be relese.
* The UserObject will be released in CCNode's destructure. * 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 * @param A user assigned CCObject
*/ */
virtual void setUserObject(CCObject *pUserObject); 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 /// @} end of Tag & User Data
private: private:
friend class geode::modifier::FieldContainer; friend class geode::modifier::FieldContainer;
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(); GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
#ifndef GEODE_IS_MEMBER_TEST
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
#endif
GEODE_DLL void addEventListenerInternal( GEODE_DLL void addEventListenerInternal(
std::string const& id, std::string const& id,
geode::EventListenerProtocol* protocol geode::EventListenerProtocol* protocol
@ -928,38 +940,6 @@ public:
*/ */
GEODE_DLL bool hasAncestor(CCNode* ancestor); 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<class T>
std::optional<T> getAttribute(std::string const& attribute) {
if (auto value = this->getAttributeInternal(attribute)) {
if (value.value().template is<T>()) {
return value.value().template as<T>();
}
}
return std::nullopt;
}
#endif
/** /**
* Set the Layout for this node. Used to automatically position children, * 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 * 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 #ifndef GEODE_IS_MEMBER_TEST
namespace geode { namespace geode {
struct GEODE_DLL AttributeSetEvent : public Event { struct GEODE_DLL UserObjectSetEvent : public Event {
cocos2d::CCNode* node; cocos2d::CCNode* node;
const std::string id; 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<AttributeSetEvent> { class GEODE_DLL AttributeSetFilter : public EventFilter<UserObjectSetEvent> {
public: public:
using Callback = void(AttributeSetEvent*); using Callback = void(UserObjectSetEvent*);
protected: protected:
std::string m_targetID; std::string m_targetID;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, AttributeSetEvent* event); ListenerResult handle(utils::MiniFunction<Callback> fn, UserObjectSetEvent* event);
AttributeSetFilter(std::string const& id); AttributeSetFilter(std::string const& id);
}; };

View file

@ -17,11 +17,10 @@ struct ProxyCCNode;
class GeodeNodeMetadata final : public cocos2d::CCObject { class GeodeNodeMetadata final : public cocos2d::CCObject {
private: private:
FieldContainer* m_fieldContainer; FieldContainer* m_fieldContainer;
Ref<cocos2d::CCObject> m_userObject;
std::string m_id = ""; std::string m_id = "";
Ref<Layout> m_layout = nullptr; Ref<Layout> m_layout = nullptr;
Ref<LayoutOptions> m_layoutOptions = nullptr; Ref<LayoutOptions> m_layoutOptions = nullptr;
std::unordered_map<std::string, matjson::Value> m_attributes; std::unordered_map<std::string, Ref<CCObject>> m_userObjects;
std::unordered_set<std::unique_ptr<EventListenerProtocol>> m_eventListeners; std::unordered_set<std::unique_ptr<EventListenerProtocol>> m_eventListeners;
std::unordered_map<std::string, std::unique_ptr<EventListenerProtocol>> m_idEventListeners; std::unordered_map<std::string, std::unique_ptr<EventListenerProtocol>> m_idEventListeners;
@ -53,8 +52,8 @@ public:
meta->retain(); meta->retain();
if (old) { if (old) {
meta->m_userObject = old; meta->m_userObjects.insert({ "", old });
// the old user object has been retained by CCNode // the old user object is now managed by Ref
old->release(); old->release();
} }
return meta; return meta;
@ -69,18 +68,24 @@ public:
#include <Geode/modify/CCNode.hpp> #include <Geode/modify/CCNode.hpp>
struct ProxyCCNode : Modify<ProxyCCNode, CCNode> { struct ProxyCCNode : Modify<ProxyCCNode, CCNode> {
virtual CCObject* getUserObject() { virtual CCObject* getUserObject() {
if (typeinfo_cast<CCNode*>(this)) { if (auto asNode = typeinfo_cast<CCNode*>(this)) {
return GeodeNodeMetadata::set(this)->m_userObject; return asNode->getUserObject("");
} }
else {
// apparently this function is the same as // apparently this function is the same as
// CCDirector::getNextScene so yeah // CCDirector::getNextScene so yeah
return m_pUserObject; return m_pUserObject;
} }
virtual void setUserObject(CCObject* obj) {
if (typeinfo_cast<CCNode*>(this)) {
GeodeNodeMetadata::set(this)->m_userObject = obj;
} }
virtual void setUserObject(CCObject* obj) {
if (auto asNode = typeinfo_cast<CCNode*>(this)) {
asNode->setUserObject("", obj);
}
else {
CC_SAFE_RELEASE(m_pUserObject);
m_pUserObject = obj; m_pUserObject = obj;
CC_SAFE_RETAIN(m_pUserObject);
}
} }
}; };
@ -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) {} : node(node), id(id), value(value) {}
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, AttributeSetEvent* event) { ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, UserObjectSetEvent* event) {
if (event->id == m_targetID) { if (event->id == m_targetID) {
fn(event); fn(event);
} }
@ -178,18 +183,23 @@ ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, AttributeSe
AttributeSetFilter::AttributeSetFilter(std::string const& id) : m_targetID(id) {} 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); auto meta = GeodeNodeMetadata::set(this);
meta->m_attributes[attr] = value; if (value) {
AttributeSetEvent(this, attr, meta->m_attributes.at(attr)).post(); meta->m_userObjects[id] = value;
}
else {
meta->m_userObjects.erase(id);
}
UserObjectSetEvent(this, id, value).post();
} }
std::optional<matjson::Value> CCNode::getAttributeInternal(std::string const& attr) { CCObject* CCNode::getUserObject(std::string const& id) {
auto meta = GeodeNodeMetadata::set(this); auto meta = GeodeNodeMetadata::set(this);
if (meta->m_attributes.count(attr)) { if (meta->m_userObjects.count(id)) {
return meta->m_attributes.at(attr); return meta->m_userObjects.at(id);
} }
return std::nullopt; return nullptr;
} }
void CCNode::addEventListenerInternal(std::string const& id, EventListenerProtocol* listener) { void CCNode::addEventListenerInternal(std::string const& id, EventListenerProtocol* listener) {

View file

@ -56,7 +56,7 @@ bool InputNode::init(
m_input->setMaxLabelScale(.85f); m_input->setMaxLabelScale(.85f);
m_input->setMaxLabelLength(maxCharCount); m_input->setMaxLabelLength(maxCharCount);
m_input->setPosition(width / 2, height / 2); 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()) { if (filter.length()) {
m_input->setAllowedChars(filter); m_input->setAllowedChars(filter);
} }

View file

@ -9,7 +9,7 @@ struct TextInputNodeFix : Modify<TextInputNodeFix, CCTextInputNode> {
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("TextInputNode fix") GEODE_FORWARD_COMPAT_DISABLE_HOOKS("TextInputNode fix")
bool ccTouchBegan(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) { bool ccTouchBegan(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) {
if (!this->template getAttribute<bool>("fix-text-input").value_or(false)) { if (!this->getUserObject("fix-text-input")) {
return CCTextInputNode::ccTouchBegan(touch, event); 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->setLabelPlaceholderColor({ 150, 150, 150 });
m_input->setLabelPlaceholderScale(.6f); m_input->setLabelPlaceholderScale(.6f);
m_input->setMaxLabelScale(.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); this->addChildAtPosition(m_input, cocos2d::Anchor::Center);
return true; return true;