2022-10-30 15:07:49 -04:00
|
|
|
#include <Geode/modify/Field.hpp>
|
2022-10-13 05:56:23 -04:00
|
|
|
#include <Geode/utils/cocos.hpp>
|
2022-10-13 04:31:23 -04:00
|
|
|
#include <Geode/modify/Field.hpp>
|
2022-10-17 09:08:12 -04:00
|
|
|
#include <Geode/modify/CCNode.hpp>
|
2022-10-30 14:56:36 -04:00
|
|
|
#include <cocos2d.h>
|
2024-05-15 04:11:46 -04:00
|
|
|
#include <queue>
|
2022-10-06 13:46:07 -04:00
|
|
|
|
2023-03-10 14:33:24 -05:00
|
|
|
using namespace geode::prelude;
|
2022-10-06 13:46:07 -04:00
|
|
|
using namespace geode::modifier;
|
|
|
|
|
|
|
|
#pragma warning(push)
|
2022-10-30 14:56:36 -04:00
|
|
|
#pragma warning(disable : 4273)
|
2022-10-06 13:46:07 -04:00
|
|
|
|
|
|
|
constexpr auto METADATA_TAG = 0xB324ABC;
|
|
|
|
|
|
|
|
struct ProxyCCNode;
|
|
|
|
|
|
|
|
class GeodeNodeMetadata final : public cocos2d::CCObject {
|
|
|
|
private:
|
2024-04-12 15:56:05 -04:00
|
|
|
std::unordered_map<std::string, FieldContainer*> m_classFieldContainers;
|
2022-10-06 13:46:07 -04:00
|
|
|
std::string m_id = "";
|
2023-03-19 09:02:49 -04:00
|
|
|
Ref<Layout> m_layout = nullptr;
|
2024-02-10 06:09:11 -05:00
|
|
|
Ref<LayoutOptions> m_layoutOptions = nullptr;
|
2024-02-22 16:39:21 -05:00
|
|
|
std::unordered_map<std::string, Ref<CCObject>> m_userObjects;
|
2023-03-29 15:07:33 -04:00
|
|
|
std::unordered_set<std::unique_ptr<EventListenerProtocol>> m_eventListeners;
|
|
|
|
std::unordered_map<std::string, std::unique_ptr<EventListenerProtocol>> m_idEventListeners;
|
2022-10-06 13:46:07 -04:00
|
|
|
|
|
|
|
friend class ProxyCCNode;
|
|
|
|
friend class cocos2d::CCNode;
|
|
|
|
|
2024-06-20 04:48:15 -04:00
|
|
|
GeodeNodeMetadata() {}
|
2022-10-30 14:56:36 -04:00
|
|
|
|
2022-10-06 13:46:07 -04:00
|
|
|
virtual ~GeodeNodeMetadata() {
|
2024-04-22 08:20:02 -04:00
|
|
|
for (auto& [_, container] : m_classFieldContainers) {
|
|
|
|
delete container;
|
|
|
|
}
|
2022-10-06 13:46:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
static GeodeNodeMetadata* set(CCNode* target) {
|
2022-10-06 15:33:56 -04:00
|
|
|
if (!target) return nullptr;
|
2022-10-30 14:56:36 -04:00
|
|
|
|
2022-10-06 13:46:07 -04:00
|
|
|
auto old = target->m_pUserObject;
|
2022-10-30 14:56:36 -04:00
|
|
|
// faster than dynamic_cast, technically can
|
2022-10-06 13:46:07 -04:00
|
|
|
// but extremely unlikely to fail
|
|
|
|
if (old && old->getTag() == METADATA_TAG) {
|
|
|
|
return static_cast<GeodeNodeMetadata*>(old);
|
|
|
|
}
|
|
|
|
auto meta = new GeodeNodeMetadata();
|
|
|
|
meta->autorelease();
|
|
|
|
meta->setTag(METADATA_TAG);
|
|
|
|
|
|
|
|
// set user object
|
|
|
|
target->m_pUserObject = meta;
|
|
|
|
meta->retain();
|
|
|
|
|
|
|
|
if (old) {
|
2024-02-22 16:39:21 -05:00
|
|
|
meta->m_userObjects.insert({ "", old });
|
|
|
|
// the old user object is now managed by Ref
|
2022-10-06 13:46:07 -04:00
|
|
|
old->release();
|
|
|
|
}
|
|
|
|
return meta;
|
|
|
|
}
|
|
|
|
|
|
|
|
FieldContainer* getFieldContainer() {
|
2024-06-20 04:48:15 -04:00
|
|
|
return nullptr;
|
2022-10-06 13:46:07 -04:00
|
|
|
}
|
2024-04-12 15:56:05 -04:00
|
|
|
|
|
|
|
FieldContainer* getFieldContainer(char const* forClass) {
|
|
|
|
if (!m_classFieldContainers.count(forClass)) {
|
|
|
|
m_classFieldContainers[forClass] = new FieldContainer();
|
|
|
|
}
|
|
|
|
return m_classFieldContainers[forClass];
|
|
|
|
}
|
2022-10-06 13:46:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// proxy forwards
|
2022-10-30 14:56:36 -04:00
|
|
|
#include <Geode/modify/CCNode.hpp>
|
2022-11-09 13:03:53 -05:00
|
|
|
struct ProxyCCNode : Modify<ProxyCCNode, CCNode> {
|
2022-10-06 13:46:07 -04:00
|
|
|
virtual CCObject* getUserObject() {
|
2024-02-22 16:39:21 -05:00
|
|
|
if (auto asNode = typeinfo_cast<CCNode*>(this)) {
|
|
|
|
return asNode->getUserObject("");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// apparently this function is the same as
|
|
|
|
// CCDirector::getNextScene so yeah
|
|
|
|
return m_pUserObject;
|
2022-11-17 15:24:37 -05:00
|
|
|
}
|
2022-10-06 13:46:07 -04:00
|
|
|
}
|
|
|
|
virtual void setUserObject(CCObject* obj) {
|
2024-02-22 16:39:21 -05:00
|
|
|
if (auto asNode = typeinfo_cast<CCNode*>(this)) {
|
|
|
|
asNode->setUserObject("", obj);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
CC_SAFE_RELEASE(m_pUserObject);
|
|
|
|
m_pUserObject = obj;
|
|
|
|
CC_SAFE_RETAIN(m_pUserObject);
|
2023-09-09 08:29:22 -04:00
|
|
|
}
|
2022-10-06 13:46:07 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-03-01 17:02:09 -05:00
|
|
|
static inline std::unordered_map<std::string, size_t> s_nextIndex;
|
|
|
|
size_t modifier::getFieldIndexForClass(char const* name) {
|
|
|
|
return s_nextIndex[name]++;
|
2022-12-03 08:51:46 -05:00
|
|
|
}
|
2022-10-30 14:56:36 -04:00
|
|
|
|
2022-10-06 13:46:07 -04:00
|
|
|
// not const because might modify contents
|
|
|
|
FieldContainer* CCNode::getFieldContainer() {
|
|
|
|
return GeodeNodeMetadata::set(this)->getFieldContainer();
|
|
|
|
}
|
|
|
|
|
2024-04-12 15:56:05 -04:00
|
|
|
FieldContainer* CCNode::getFieldContainer(char const* forClass) {
|
|
|
|
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
|
|
|
|
}
|
|
|
|
|
2022-10-06 13:46:07 -04:00
|
|
|
std::string CCNode::getID() {
|
|
|
|
return GeodeNodeMetadata::set(this)->m_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CCNode::setID(std::string const& id) {
|
|
|
|
GeodeNodeMetadata::set(this)->m_id = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCNode* CCNode::getChildByID(std::string const& id) {
|
2024-08-01 05:31:16 -04:00
|
|
|
for (auto child : CCArrayExt<CCNode*>(this->getChildren())) {
|
2022-10-06 13:46:07 -04:00
|
|
|
if (child->getID() == id) {
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
|
|
|
|
if (auto child = this->getChildByID(id)) {
|
|
|
|
return child;
|
|
|
|
}
|
2024-01-09 08:54:29 -05:00
|
|
|
for (auto child : CCArrayExt<CCNode*>(m_pChildren)) {
|
2022-10-08 05:41:36 -04:00
|
|
|
if ((child = child->getChildByIDRecursive(id))) {
|
2022-10-06 13:46:07 -04:00
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2024-05-15 04:11:46 -04:00
|
|
|
class BFSNodeTreeCrawler final {
|
|
|
|
private:
|
|
|
|
std::queue<CCNode*> m_queue;
|
|
|
|
std::unordered_set<CCNode*> m_explored;
|
|
|
|
|
|
|
|
public:
|
|
|
|
BFSNodeTreeCrawler(CCNode* target) {
|
|
|
|
if (auto first = getChild(target, 0)) {
|
|
|
|
m_explored.insert(first);
|
|
|
|
m_queue.push(first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CCNode* next() {
|
|
|
|
if (m_queue.empty()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
auto node = m_queue.front();
|
|
|
|
m_queue.pop();
|
|
|
|
for (auto sibling : CCArrayExt<CCNode*>(node->getParent()->getChildren())) {
|
|
|
|
if (!m_explored.contains(sibling)) {
|
|
|
|
m_explored.insert(sibling);
|
|
|
|
m_queue.push(sibling);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (auto child : CCArrayExt<CCNode*>(node->getChildren())) {
|
|
|
|
if (!m_explored.contains(child)) {
|
|
|
|
m_explored.insert(child);
|
|
|
|
m_queue.push(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class NodeQuery final {
|
|
|
|
private:
|
|
|
|
enum class Op {
|
|
|
|
ImmediateChild,
|
|
|
|
DescendantChild,
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string m_targetID;
|
|
|
|
Op m_nextOp;
|
|
|
|
std::unique_ptr<NodeQuery> m_next = nullptr;
|
|
|
|
|
|
|
|
public:
|
|
|
|
static Result<std::unique_ptr<NodeQuery>> parse(std::string const& query) {
|
|
|
|
if (query.empty()) {
|
|
|
|
return Err("Query may not be empty");
|
|
|
|
}
|
|
|
|
|
|
|
|
auto result = std::make_unique<NodeQuery>();
|
|
|
|
NodeQuery* current = result.get();
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
std::string collectedID;
|
|
|
|
std::optional<Op> nextOp = Op::DescendantChild;
|
|
|
|
while (i < query.size()) {
|
|
|
|
auto c = query.at(i);
|
|
|
|
if (c == ' ') {
|
|
|
|
if (!nextOp) {
|
|
|
|
nextOp.emplace(Op::DescendantChild);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (c == '>') {
|
|
|
|
if (!nextOp || *nextOp == Op::DescendantChild) {
|
|
|
|
nextOp.emplace(Op::ImmediateChild);
|
|
|
|
}
|
|
|
|
// Double >> is syntax error
|
|
|
|
else {
|
|
|
|
return Err("Can't have multiple child operators at once (index {})", i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// ID-valid characters
|
|
|
|
else if (std::isalnum(c) || c == '-' || c == '_' || c == '/' || c == '.') {
|
|
|
|
if (nextOp) {
|
|
|
|
current->m_next = std::make_unique<NodeQuery>();
|
|
|
|
current->m_nextOp = *nextOp;
|
|
|
|
current->m_targetID = collectedID;
|
|
|
|
current = current->m_next.get();
|
|
|
|
|
|
|
|
collectedID = "";
|
|
|
|
nextOp = std::nullopt;
|
|
|
|
}
|
|
|
|
collectedID.push_back(c);
|
|
|
|
}
|
|
|
|
// Any other character is syntax error due to needing to reserve
|
|
|
|
// stuff for possible future features
|
|
|
|
else {
|
|
|
|
return Err("Unexpected character '{}' at index {}", c, i);
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
if (nextOp || collectedID.empty()) {
|
|
|
|
return Err("Expected node ID but got end of query");
|
|
|
|
}
|
|
|
|
current->m_targetID = collectedID;
|
|
|
|
|
|
|
|
return Ok(std::move(result));
|
|
|
|
}
|
|
|
|
|
|
|
|
CCNode* match(CCNode* node) const {
|
|
|
|
// Make sure this matches the ID being looked for
|
|
|
|
if (!m_targetID.empty() && node->getID() != m_targetID) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
// If this is the last thing to match, return the result
|
|
|
|
if (!m_next) {
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
switch (m_nextOp) {
|
|
|
|
case Op::ImmediateChild: {
|
|
|
|
for (auto c : CCArrayExt<CCNode*>(node->getChildren())) {
|
|
|
|
if (auto r = m_next->match(c)) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case Op::DescendantChild: {
|
|
|
|
auto crawler = BFSNodeTreeCrawler(node);
|
|
|
|
while (auto c = crawler.next()) {
|
|
|
|
if (auto r = m_next->match(c)) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string toString() const {
|
|
|
|
auto str = m_targetID.empty() ? "&" : m_targetID;
|
|
|
|
if (m_next) {
|
|
|
|
switch (m_nextOp) {
|
|
|
|
case Op::ImmediateChild: str += " > "; break;
|
|
|
|
case Op::DescendantChild: str += " "; break;
|
|
|
|
}
|
|
|
|
str += m_next->toString();
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
CCNode* CCNode::querySelector(std::string const& queryStr) {
|
|
|
|
auto res = NodeQuery::parse(queryStr);
|
|
|
|
if (!res) {
|
|
|
|
log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
auto query = std::move(res.unwrap());
|
|
|
|
log::info("parsed query: {}", query->toString());
|
|
|
|
return query->match(this);
|
|
|
|
}
|
|
|
|
|
2023-03-11 03:19:26 -05:00
|
|
|
void CCNode::removeChildByID(std::string const& id) {
|
|
|
|
if (auto child = this->getChildByID(id)) {
|
|
|
|
this->removeChild(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 10:08:13 -05:00
|
|
|
void CCNode::setLayout(Layout* layout, bool apply, bool respectAnchor) {
|
|
|
|
if (respectAnchor && this->isIgnoreAnchorPointForPosition()) {
|
2024-01-09 08:54:29 -05:00
|
|
|
for (auto child : CCArrayExt<CCNode*>(m_pChildren)) {
|
2023-02-02 10:08:13 -05:00
|
|
|
child->setPosition(child->getPosition() + this->getScaledContentSize());
|
|
|
|
}
|
|
|
|
this->ignoreAnchorPointForPosition(false);
|
|
|
|
}
|
2023-03-19 09:02:49 -04:00
|
|
|
GeodeNodeMetadata::set(this)->m_layout = layout;
|
2022-10-10 13:58:47 -04:00
|
|
|
if (apply) {
|
|
|
|
this->updateLayout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Layout* CCNode::getLayout() {
|
2023-03-19 09:02:49 -04:00
|
|
|
return GeodeNodeMetadata::set(this)->m_layout.data();
|
2022-10-10 13:58:47 -04:00
|
|
|
}
|
|
|
|
|
2023-02-06 14:36:08 -05:00
|
|
|
void CCNode::setLayoutOptions(LayoutOptions* options, bool apply) {
|
2024-02-10 06:09:11 -05:00
|
|
|
GeodeNodeMetadata::set(this)->m_layoutOptions = options;
|
2023-02-06 14:36:08 -05:00
|
|
|
if (apply && m_pParent) {
|
|
|
|
m_pParent->updateLayout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LayoutOptions* CCNode::getLayoutOptions() {
|
2024-02-10 06:09:11 -05:00
|
|
|
return GeodeNodeMetadata::set(this)->m_layoutOptions.data();
|
2023-02-06 14:36:08 -05:00
|
|
|
}
|
|
|
|
|
2023-02-11 05:06:37 -05:00
|
|
|
void CCNode::updateLayout(bool updateChildOrder) {
|
|
|
|
if (updateChildOrder) {
|
|
|
|
this->sortAllChildren();
|
|
|
|
}
|
2023-03-19 09:02:49 -04:00
|
|
|
if (auto layout = GeodeNodeMetadata::set(this)->m_layout.data()) {
|
2023-02-02 10:08:13 -05:00
|
|
|
layout->apply(this);
|
2022-10-10 13:58:47 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-22 16:39:21 -05:00
|
|
|
UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value)
|
2023-03-29 09:05:20 -04:00
|
|
|
: node(node), id(id), value(value) {}
|
|
|
|
|
2024-02-22 16:39:21 -05:00
|
|
|
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, UserObjectSetEvent* event) {
|
2023-03-29 09:05:20 -04:00
|
|
|
if (event->id == m_targetID) {
|
|
|
|
fn(event);
|
|
|
|
}
|
|
|
|
return ListenerResult::Propagate;
|
|
|
|
}
|
|
|
|
|
|
|
|
AttributeSetFilter::AttributeSetFilter(std::string const& id) : m_targetID(id) {}
|
|
|
|
|
2024-02-22 16:39:21 -05:00
|
|
|
void CCNode::setUserObject(std::string const& id, CCObject* value) {
|
2023-03-29 09:05:20 -04:00
|
|
|
auto meta = GeodeNodeMetadata::set(this);
|
2024-02-22 16:39:21 -05:00
|
|
|
if (value) {
|
|
|
|
meta->m_userObjects[id] = value;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
meta->m_userObjects.erase(id);
|
|
|
|
}
|
|
|
|
UserObjectSetEvent(this, id, value).post();
|
2022-10-25 15:57:40 -04:00
|
|
|
}
|
|
|
|
|
2024-02-22 16:39:21 -05:00
|
|
|
CCObject* CCNode::getUserObject(std::string const& id) {
|
2022-10-25 15:57:40 -04:00
|
|
|
auto meta = GeodeNodeMetadata::set(this);
|
2024-02-22 16:39:21 -05:00
|
|
|
if (meta->m_userObjects.count(id)) {
|
|
|
|
return meta->m_userObjects.at(id);
|
2022-10-25 15:57:40 -04:00
|
|
|
}
|
2024-02-22 16:39:21 -05:00
|
|
|
return nullptr;
|
2022-10-25 15:57:40 -04:00
|
|
|
}
|
|
|
|
|
2023-03-29 15:07:33 -04:00
|
|
|
void CCNode::addEventListenerInternal(std::string const& id, EventListenerProtocol* listener) {
|
|
|
|
auto meta = GeodeNodeMetadata::set(this);
|
|
|
|
if (id.size()) {
|
|
|
|
if (meta->m_idEventListeners.contains(id)) {
|
|
|
|
meta->m_idEventListeners.at(id).reset(listener);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
meta->m_idEventListeners.emplace(id, listener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
std::erase_if(meta->m_eventListeners, [=](auto& l) {
|
|
|
|
return l.get() == listener;
|
|
|
|
});
|
|
|
|
meta->m_eventListeners.emplace(listener);
|
|
|
|
}
|
2023-03-25 12:22:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void CCNode::removeEventListener(EventListenerProtocol* listener) {
|
2023-03-29 15:07:33 -04:00
|
|
|
auto meta = GeodeNodeMetadata::set(this);
|
|
|
|
std::erase_if(meta->m_eventListeners, [=](auto& l) {
|
|
|
|
return l.get() == listener;
|
|
|
|
});
|
|
|
|
std::erase_if(meta->m_idEventListeners, [=](auto& l) {
|
|
|
|
return l.second.get() == listener;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void CCNode::removeEventListener(std::string const& id) {
|
|
|
|
GeodeNodeMetadata::set(this)->m_idEventListeners.erase(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
EventListenerProtocol* CCNode::getEventListener(std::string const& id) {
|
|
|
|
auto meta = GeodeNodeMetadata::set(this);
|
|
|
|
if (meta->m_idEventListeners.contains(id)) {
|
|
|
|
return meta->m_idEventListeners.at(id).get();
|
|
|
|
}
|
|
|
|
return nullptr;
|
2023-03-23 15:42:01 -04:00
|
|
|
}
|
|
|
|
|
2023-04-05 12:26:46 -04:00
|
|
|
size_t CCNode::getEventListenerCount() {
|
|
|
|
return GeodeNodeMetadata::set(this)->m_idEventListeners.size() +
|
|
|
|
GeodeNodeMetadata::set(this)->m_eventListeners.size();
|
|
|
|
}
|
|
|
|
|
2024-01-31 16:30:52 -05:00
|
|
|
void CCNode::addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset, bool useAnchorLayout) {
|
2024-02-22 10:28:09 -05:00
|
|
|
return this->addChildAtPosition(child, anchor, offset, child->getAnchorPoint(), useAnchorLayout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CCNode::addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset, CCPoint const& nodeAnchor, bool useAnchorLayout) {
|
2024-01-31 16:11:43 -05:00
|
|
|
auto layout = this->getLayout();
|
2024-01-31 16:30:52 -05:00
|
|
|
if (!layout && useAnchorLayout) {
|
2024-01-31 16:11:43 -05:00
|
|
|
this->setLayout(AnchorLayout::create());
|
|
|
|
}
|
2024-02-22 10:28:09 -05:00
|
|
|
// Set the position
|
2024-01-31 16:11:43 -05:00
|
|
|
child->setPosition(AnchorLayout::getAnchoredPosition(this, anchor, offset));
|
2024-02-22 10:28:09 -05:00
|
|
|
child->setAnchorPoint(nodeAnchor);
|
|
|
|
// Set dynamic positioning
|
2024-01-31 16:30:52 -05:00
|
|
|
if (useAnchorLayout) {
|
|
|
|
child->setLayoutOptions(AnchorLayoutOptions::create()->setAnchor(anchor)->setOffset(offset));
|
|
|
|
}
|
2024-01-31 16:11:43 -05:00
|
|
|
this->addChild(child);
|
2024-01-29 17:08:53 -05:00
|
|
|
}
|
|
|
|
|
2024-02-22 10:28:09 -05:00
|
|
|
void CCNode::updateAnchoredPosition(Anchor anchor, CCPoint const& offset) {
|
|
|
|
return this->updateAnchoredPosition(anchor, offset, this->getAnchorPoint());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CCNode::updateAnchoredPosition(Anchor anchor, CCPoint const& offset, CCPoint const& nodeAnchor) {
|
|
|
|
// Always require a parent
|
|
|
|
if (!m_pParent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Set the position
|
|
|
|
this->setPosition(AnchorLayout::getAnchoredPosition(m_pParent, anchor, offset));
|
|
|
|
this->setAnchorPoint(nodeAnchor);
|
|
|
|
// Update dynamic positioning
|
|
|
|
if (auto opts = typeinfo_cast<AnchorLayoutOptions*>(this->getLayoutOptions())) {
|
|
|
|
opts->setAnchor(anchor);
|
|
|
|
opts->setOffset(offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-25 05:53:49 -05:00
|
|
|
#ifdef GEODE_EXPORTING
|
|
|
|
|
|
|
|
void CCNode::setAttribute(std::string const& attr, matjson::Value const& value) {}
|
|
|
|
std::optional<matjson::Value> CCNode::getAttributeInternal(std::string const& attr) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2022-10-06 13:46:07 -04:00
|
|
|
#pragma warning(pop)
|