Merge pull request #150 from geode-sdk/addEventListener

Event stuff
This commit is contained in:
HJfod 2023-04-01 21:40:00 +03:00 committed by GitHub
commit 61015be6b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 451 additions and 69 deletions

View file

@ -146,7 +146,7 @@ endif()
add_library(GeodeCodegenSources ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/Geode/GeneratedAddress.cpp)
target_link_directories(GeodeCodegenSources PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/loader/include/link)
target_link_libraries(GeodeCodegenSources PRIVATE ghc_filesystem GeodeFilesystemImpl fmt TulipHookInclude)
target_link_libraries(GeodeCodegenSources PRIVATE ghc_filesystem GeodeFilesystemImpl fmt TulipHookInclude mat-json)
target_include_directories(GeodeCodegenSources PRIVATE
${GEODE_CODEGEN_PATH}
${GEODE_LOADER_PATH}/include

View file

@ -408,12 +408,12 @@ class cocos2d::CCMenu {
virtual auto registerWithTouchDispatcher() = mac 0x438cd0, ios 0x131f8c;
virtual auto onExit() = mac 0x438bd0, ios 0x131ed4;
virtual auto removeChild(cocos2d::CCNode*, bool) = mac 0x438c20, ios 0x15e630;
auto initWithArray(cocos2d::CCArray*) = mac 0x4389f0, ios 0x131d04;
auto itemForTouch(cocos2d::CCTouch*) = mac 0x438dd0;
bool initWithArray(cocos2d::CCArray*) = mac 0x4389f0, ios 0x131d04;
cocos2d::CCMenuItem* itemForTouch(cocos2d::CCTouch*) = mac 0x438dd0;
}
class cocos2d::CCMenuItem {
auto initWithTarget(cocos2d::CCObject*, cocos2d::SEL_MenuHandler) = mac 0x1fb7f0;
bool initWithTarget(cocos2d::CCObject*, cocos2d::SEL_MenuHandler) = mac 0x1fb7f0;
virtual ~CCMenuItem() = mac 0x1fb8e0, ios 0x2cdf4;
virtual auto activate() = mac 0x1fba70, ios 0x2ceb0;
virtual auto selected() = mac 0x1fb9e0, ios 0x2ce2e;
@ -636,9 +636,6 @@ class cocos2d::CCObject {
auto isEqual(cocos2d::CCObject const*) = mac 0x250f20, ios 0x439e4;
auto release() = mac 0x250ea0, ios 0x43984;
auto retain() = mac 0x250ec0, ios 0x439a8;
unsigned int retainCount() const {
return m_uReference;
}
virtual auto setTag(int) = mac 0x250f60, ios 0x43a10;
~CCObject() = mac 0x250d20, ios 0x6ac0;

View file

@ -97,7 +97,10 @@ class AppDelegate : cocos2d::CCApplication, cocos2d::CCSceneDelegate {
void resumeSound() = win 0x3d4d0;
void setupGLView() = win 0x3c950;
PAD = win 0x4;
cocos2d::CCScene* m_runningScene;
bool m_loadingFinished;
// there's 0x18 more on Windows
}
class ArtistCell : TableViewCell {
@ -1245,7 +1248,7 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ
virtual void scaleChanged(float) = mac 0x25490, win 0x88df0;
virtual void scaleChangeEnded() = win 0x88de0;
void scaleObjects(cocos2d::CCArray*, float, cocos2d::CCPoint) = mac 0x252e0, win 0x8f150;
void selectObjects(cocos2d::CCArray*, bool) = mac 0x23940, win 0x864a0;
void selectObjects(cocos2d::CCArray* objs, bool ignoreFilters) = mac 0x23940, win 0x864a0;
void setupCreateMenu() = mac 0xcb50, win 0x7caf0;
void undoLastAction(cocos2d::CCObject*) = mac 0xb830, win 0x87070;
void updateButtons() = mac 0x1a300, win 0x78280;
@ -2355,7 +2358,12 @@ class GJGroundLayer : cocos2d::CCLayer {
void updateGroundWidth() = mac 0x356790, win 0x12dda0;
}
class GJItemIcon {
class GJItemIcon : cocos2d::CCSprite {
bool init(
UnlockType, int, cocos2d::ccColor3B, cocos2d::ccColor3B,
bool, bool, bool, cocos2d::ccColor3B
) = win 0x12ccf0;
GJItemIcon* createBrowserIcon(UnlockType _type, int _id) {
return GJItemIcon::create(_type, _id,
{ 0xaf, 0xaf, 0xaf }, { 0xff, 0xff, 0xff },
@ -3183,8 +3191,8 @@ class GameObject : CCSpritePlus {
bool m_isEffectObject;
bool m_randomisedAnimStart;
float m_animSpeed;
bool m_blackChild;
bool m_unkOutlineMaybe;
bool m_isBlackObject;
bool m_isBlackObjectWithOutline;
float m_blackChildOpacity;
bool field_21C;
bool m_editor;
@ -3387,6 +3395,7 @@ class GameStatsManager : cocos2d::CCNode {
void storePendingUserCoin(char const*) = mac 0x42940;
void storeSecretCoin(char const*) = mac 0x42a10;
void storeUserCoin(char const*) = mac 0x42890;
bool isItemUnlocked(UnlockType type, int id) = win 0xfbb80;
PAD = win 0x28;
cocos2d::CCDictionary* m_dailyChests;

View file

@ -168,7 +168,7 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl
}
std::string supers = str_if(
fmt::format(" : public {}", fmt::join(cls.superclasses, ", ")),
fmt::format(" : public {}", fmt::join(cls.superclasses, ", public ")),
!cls.superclasses.empty()
);

View file

@ -285,6 +285,12 @@ public:
*/
void resumeTargets(CCSet* targetsToResume);
/**
* Get the shared scheduler from CCDirector
* @note Geode addition
*/
static GEODE_DLL CCScheduler* get();
private:
void removeHashElement(struct _hashSelectorEntry *pElement);
void removeUpdateFromHash(struct _listEntry *entry);

View file

@ -38,7 +38,8 @@
#include "../script_support/CCScriptSupport.h"
#include "../include/CCProtocols.h"
#include "Layout.hpp"
#include <any>
#include "../../loader/Event.hpp"
#include <json.hpp>
NS_CC_BEGIN
@ -850,7 +851,11 @@ private:
friend class geode::modifier::FieldContainer;
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
GEODE_DLL std::optional<std::any> getAttributeInternal(std::string const& attribute);
GEODE_DLL std::optional<json::Value> getAttributeInternal(std::string const& attribute);
GEODE_DLL void addEventListenerInternal(
std::string const& id,
geode::EventListenerProtocol* protocol
);
public:
/**
@ -892,6 +897,7 @@ public:
* @param before The child the node is added before of. If this is null or
* not a child of this node, the new child will be placed at the start of the
* child list
* @note Geode addition
*/
GEODE_DLL void insertBefore(CCNode* child, CCNode* before);
@ -902,9 +908,20 @@ public:
* @param after The child the node is added after of. If this is null or
* not a child of this node, the new child will be placed at the end of the
* child list
* @note Geode addition
*/
GEODE_DLL void insertAfter(CCNode* child, CCNode* after);
/**
* Check if this node's parent or its parents' parent is the given node
* @param ancestor The node whose child or subchild this node should be. If
* nullptr, returns true if the node is in the current scene, otherwise
* false.
* @returns True if ancestor is an ancestor of this node
* @note Geode addition
*/
GEODE_DLL bool hasAncestor(CCNode* ancestor);
/**
* 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
@ -917,7 +934,7 @@ public:
* @param value The value of the attribute
* @note Geode addition
*/
GEODE_DLL void setAttribute(std::string const& attribute, std::any value);
GEODE_DLL void setAttribute(std::string const& attribute, json::Value const& value);
/**
* Get an attribute from the node. Attributes may be anything
* @param attribute The attribute key
@ -929,7 +946,7 @@ public:
std::optional<T> getAttribute(std::string const& attribute) {
if (auto value = this->getAttributeInternal(attribute)) {
try {
return std::any_cast<T>(value.value());
return value.value().template as<T>();
} catch(...) {
return std::nullopt;
}
@ -986,6 +1003,31 @@ public:
* @note Geode addition
*/
GEODE_DLL void swapChildIndices(CCNode* first, CCNode* second);
template <class Filter, class... Args>
geode::EventListenerProtocol* addEventListener(
std::string const& id,
geode::utils::MiniFunction<typename Filter::Callback> callback,
Args&&... args
) {
auto listener = new geode::EventListener<Filter>(
callback, Filter(this, std::forward<Args>(args)...)
);
this->addEventListenerInternal(id, listener);
return listener;
}
template <class Filter, class... Args>
geode::EventListenerProtocol* addEventListener(
geode::utils::MiniFunction<typename Filter::Callback> callback,
Args&&... args
) {
return this->template addEventListener<Filter, Args...>(
"", callback, std::forward<Args>(args)...
);
}
GEODE_DLL void removeEventListener(geode::EventListenerProtocol* listener);
GEODE_DLL void removeEventListener(std::string const& id);
GEODE_DLL geode::EventListenerProtocol* getEventListener(std::string const& id);
/// @{
/// @name Shader Program
@ -1690,4 +1732,27 @@ protected:
NS_CC_END
namespace geode {
struct GEODE_DLL AttributeSetEvent : public Event {
cocos2d::CCNode* node;
const std::string id;
json::Value& value;
AttributeSetEvent(cocos2d::CCNode* node, std::string const& id, json::Value& value);
};
class GEODE_DLL AttributeSetFilter : public EventFilter<AttributeSetEvent> {
public:
using Callback = void(AttributeSetEvent*);
protected:
std::string m_targetID;
public:
ListenerResult handle(utils::MiniFunction<Callback> fn, AttributeSetEvent* event);
AttributeSetFilter(std::string const& id);
};
}
#endif // __PLATFORM_CCNODE_H__

View file

@ -100,16 +100,14 @@ public:
int m_nLuaID;
protected:
// the object's tag
RT_ADD( int m_nTag; )
int m_nTag;
// count of references
unsigned int m_uReference;
// count of autorelease
unsigned int m_uAutoReleaseCount;
RT_ADD(
CCObjectType m_eObjType;
int m_nUnknown;
)
CCObjectType m_eObjType;
int m_nUnknown;
public:
GEODE_CUSTOM_CONSTRUCTOR_BEGIN(CCObject)
CCObject(void);
@ -123,7 +121,9 @@ public:
CCObject* autorelease(void);
CCObject* copy(void);
bool isSingleReference(void) const;
inline unsigned int retainCount(void) const;
inline unsigned int retainCount(void) const {
return m_uReference;
}
virtual bool isEqual(const CCObject* pObject);
virtual void acceptVisitor(CCDataVisitor &visitor);

View file

@ -71,17 +71,19 @@ public:
virtual ~CCScene();
bool init();
static CCScene *create(void);
static CCScene* create(void);
/**
* Get the running scene
* @note Geode addition
*/
static GEODE_DLL CCScene* get();
RT_ADD(
CCScene(const CCScene&);
CCScene& operator=(const CCScene&);
CCScene(const CCScene&);
CCScene& operator=(const CCScene&);
int getHighestChildZ(void);
int getHighestChildZ(void);
CCSceneDelegate* m_pDelegate;
)
CCSceneDelegate* m_pDelegate;
};
// end of scene group

View file

@ -42,14 +42,11 @@ public:
*/
virtual TargetPlatform getTargetPlatform();
RT_ADD(
virtual void openURL(const char* url);
virtual int run();
virtual void setupGLView();
virtual void platformShutdown();
void toggleVerticalSync(bool);
bool getVerticalSyncEnabled() const;
)
virtual void openURL(const char* url);
virtual int run();
virtual void setupGLView();
virtual void platformShutdown();
void toggleVerticalSync(bool);
/**
* Sets the Resource root path.
@ -65,8 +62,6 @@ public:
void setStartupScriptFilename(const gd::string& startupScriptFile);
bool getControllerConnected() const;
const gd::string& getStartupScriptFilename(void)
{
return m_startupScriptFilename;
@ -76,8 +71,24 @@ protected:
HINSTANCE m_hInstance;
HACCEL m_hAccelTable;
LARGE_INTEGER m_nAnimationInterval;
gd::string m_resourceRootPath;
gd::string m_startupScriptFilename;
PAD(4);
std::string m_resourceRootPath;
std::string m_startupScriptFilename;
void* m_pUnknown;
bool m_bUpdateController;
CC_SYNTHESIZE_NV(bool, m_bShutdownCalled, ShutdownCalled);
INPUT m_iInput;
CCPoint m_obUnknown1;
CCPoint m_obUnknown2;
bool m_bMouseControl;
float m_fOldAnimationInterval;
float m_fAnimationInterval;
CC_SYNTHESIZE_READONLY_NV(bool, m_bVerticalSyncEnabled, VerticalSyncEnabled);
CC_SYNTHESIZE_READONLY_NV(bool, m_bControllerConnected, ControllerConnected);
CC_SYNTHESIZE_NV(bool, m_bSleepMode, SleepMode);
CC_SYNTHESIZE_NV(bool, m_bForceTimer, ForceTimer);
CC_SYNTHESIZE_NV(bool, m_bSmoothFix, SmoothFix);
CC_SYNTHESIZE_NV(bool, m_bFullscreen, Fullscreen);
static CCApplication * sm_pSharedApplication;
};

View file

@ -47,13 +47,13 @@ typedef enum
} ccTouchSelectorFlag;
enum {
CCTOUCHBEGAN,
CCTOUCHMOVED,
CCTOUCHENDED,
CCTOUCHCANCELLED,
enum ccTouchType {
CCTOUCHBEGAN = 0,
CCTOUCHMOVED = 1,
CCTOUCHENDED = 2,
CCTOUCHCANCELLED = 3,
ccTouchMax,
ccTouchMax = 4,
};
class CCSet;

View file

@ -1,6 +1,7 @@
#pragma once
#include "../utils/casts.hpp"
#include "../utils/MiniFunction.hpp"
#include <Geode/DefaultInclude.hpp>
#include <type_traits>
@ -119,6 +120,10 @@ namespace geode {
m_filter = filter;
}
T getFilter() const {
return m_filter;
}
protected:
utils::MiniFunction<Callback> m_callback = nullptr;
T m_filter;
@ -126,7 +131,6 @@ namespace geode {
class GEODE_DLL [[nodiscard]] Event {
private:
static std::unordered_set<EventListenerProtocol*>& listeners();
friend EventListenerProtocol;
public:
@ -137,7 +141,14 @@ namespace geode {
void post() {
postFrom(getMod());
}
virtual ~Event();
static std::vector<EventListenerProtocol*>& listeners();
/**
* Move an event listener to the front of the queue so it is always hit
* first
*/
static void prioritize(EventListenerProtocol* listener);
};
}

View file

@ -71,6 +71,24 @@ namespace geode {
return "nullopt";
}
template <class T>
requires requires(T t) {
parse(t);
}
std::string parse(std::vector<T> const& thing) {
std::string res = "[";
bool first = true;
for (auto& t : thing) {
if (!first) {
res += ", ";
}
first = false;
res += parse(t);
}
res += "]";
return res;
}
template <class A, class B>
requires requires(A a, B b) {
parse(a);

View file

@ -203,6 +203,7 @@ namespace geode {
CircleBaseColor color = CircleBaseColor::Green,
CircleBaseSize size = CircleBaseSize::Medium
);
cocos2d::CCSize getMaxTopSize() const override;
};
/**

View file

@ -356,6 +356,151 @@ namespace geode {
}
};
class GEODE_DLL WeakRefPool {
std::unordered_set<cocos2d::CCObject*> m_pool;
public:
static WeakRefPool* get();
bool isManaged(cocos2d::CCObject* obj);
void manage(cocos2d::CCObject* obj);
void check(cocos2d::CCObject* obj);
};
/**
* A smart pointer to a managed CCObject-deriving class. Like Ref, except
* only holds a weak reference to the targeted object. When all non-weak
* references (Refs, manual retain() calls) to the object are dropped, so
* are all weak references.
*
* In essence, WeakRef is like a raw pointer, except that you can know if
* the pointer is still valid or not, as WeakRef::lock() returns nullptr if
* the pointed-to-object has already been freed.
*
* Note that an object pointed to by WeakRef is only released once some
* WeakRef pointing to it checks for it after all other references to the
* object have been dropped. If you store WeakRefs in a global map, you may
* want to periodically lock all of them to make sure any memory that should
* be freed is freed.
*
* @tparam T A type that inherits from CCObject.
*/
template <class T>
class WeakRef final {
static_assert(
std::is_base_of_v<cocos2d::CCObject, T>,
"WeakRef can only be used with a CCObject-inheriting class!"
);
T* m_obj = nullptr;
public:
/**
* Construct a WeakRef of an object. A weak reference is one that will
* be valid as long as the object is referenced by other strong
* references (such as Ref or manual retain calls), but once all strong
* references are dropped, so are all weak references. The object is
* freed once no strong references exist to it, and any WeakRef pointing
* to it is freed or locked
* @param obj Object to construct the WeakRef from
*/
WeakRef(T* obj) : m_obj(obj) {
WeakRefPool::get()->manage(obj);
}
WeakRef(WeakRef<T> const& other) : WeakRef(other.m_obj) {}
WeakRef(WeakRef<T>&& other) : m_obj(other.m_obj) {
other.m_obj = nullptr;
}
/**
* Construct an empty WeakRef (the object will be null)
*/
WeakRef() = default;
~WeakRef() {
WeakRefPool::get()->check(m_obj);
}
/**
* Lock the WeakRef, returning a Ref if the pointed object is valid or
* a null Ref if the object has been freed
*/
Ref<T> lock() const {
if (WeakRefPool::get()->isManaged(m_obj)) {
return Ref(m_obj);
}
return Ref<T>(nullptr);
}
/**
* Check if the WeakRef points to a valid object
*/
bool valid() const {
return WeakRefPool::get()->isManaged(m_obj);
}
/**
* Swap the managed object with another object. The managed object
* will be released, and the new object retained
* @param other The new object to swap to
*/
void swap(T* other) {
WeakRefPool::get()->check(m_obj);
m_obj = other;
WeakRefPool::get()->manage(other);
}
Ref<T> operator=(T* obj) {
this->swap(obj);
return this->lock();
}
WeakRef<T>& operator=(WeakRef<T> const& other) {
this->swap(other.m_obj);
return *this;
}
WeakRef<T>& operator=(WeakRef<T>&& other) {
this->swap(other.m_obj);
return *this;
}
explicit operator bool() const noexcept {
return this->valid();
}
bool operator==(T* other) const {
return m_obj == other;
}
bool operator==(WeakRef<T> const& other) const {
return m_obj == other.m_obj;
}
bool operator!=(T* other) const {
return m_obj != other;
}
bool operator!=(WeakRef<T> const& other) const {
return m_obj != other.m_obj;
}
// for containers
bool operator<(WeakRef<T> const& other) const {
return m_obj < other.m_obj;
}
bool operator<=(WeakRef<T> const& other) const {
return m_obj <= other.m_obj;
}
bool operator>(WeakRef<T> const& other) const {
return m_obj > other.m_obj;
}
bool operator>=(WeakRef<T> const& other) const {
return m_obj >= other.m_obj;
}
};
template <class Filter>
class EventListenerNode : public cocos2d::CCNode {
protected:
@ -375,8 +520,8 @@ namespace geode {
return nullptr;
}
static EventListenerNode* create(typename Filter::Callback callback) {
auto ret = new EventListenerNode(EventListener<Filter>(callback));
static EventListenerNode* create(typename Filter::Callback callback, Filter filter = Filter()) {
auto ret = new EventListenerNode(EventListener<Filter>(callback, filter));
if (ret && ret->init()) {
ret->autorelease();
return ret;
@ -385,7 +530,6 @@ namespace geode {
return nullptr;
}
template <class C>
static EventListenerNode* create(
C* cls, typename EventListener<Filter>::template MemberFn<C> callback

View file

@ -37,4 +37,12 @@ CCTextureCache* CCTextureCache::get() {
return CCTextureCache::sharedTextureCache();
}
CCScene* CCScene::get() {
return CCDirector::get()->getRunningScene();
}
CCScheduler* CCScheduler::get() {
return CCDirector::get()->getScheduler();
}
#pragma warning(pop)

View file

@ -37,6 +37,19 @@ void CCNode::insertAfter(CCNode* child, CCNode* after) {
}
}
bool CCNode::hasAncestor(CCNode* ancestor) {
if (!ancestor) {
ancestor = CCScene::get();
}
if (m_pParent == ancestor) {
return true;
}
if (m_pParent) {
return m_pParent->hasAncestor(ancestor);
}
return false;
}
CCArray* Layout::getNodesToPosition(CCNode* on) {
if (!on->getChildren()) {
return CCArray::create();

View file

@ -21,7 +21,9 @@ private:
std::string m_id = "";
Ref<Layout> m_layout = nullptr;
std::unique_ptr<LayoutOptions> m_layoutOptions = nullptr;
std::unordered_map<std::string, std::any> m_attributes;
std::unordered_map<std::string, json::Value> m_attributes;
std::unordered_set<std::unique_ptr<EventListenerProtocol>> m_eventListeners;
std::unordered_map<std::string, std::unique_ptr<EventListenerProtocol>> m_idEventListeners;
friend class ProxyCCNode;
friend class cocos2d::CCNode;
@ -165,11 +167,25 @@ void CCNode::updateLayout(bool updateChildOrder) {
}
}
void CCNode::setAttribute(std::string const& attr, std::any value) {
GeodeNodeMetadata::set(this)->m_attributes[attr] = value;
AttributeSetEvent::AttributeSetEvent(CCNode* node, std::string const& id, json::Value& value)
: node(node), id(id), value(value) {}
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, AttributeSetEvent* event) {
if (event->id == m_targetID) {
fn(event);
}
return ListenerResult::Propagate;
}
std::optional<std::any> CCNode::getAttributeInternal(std::string const& attr) {
AttributeSetFilter::AttributeSetFilter(std::string const& id) : m_targetID(id) {}
void CCNode::setAttribute(std::string const& attr, json::Value const& value) {
auto meta = GeodeNodeMetadata::set(this);
meta->m_attributes[attr] = value;
AttributeSetEvent(this, attr, meta->m_attributes.at(attr)).post();
}
std::optional<json::Value> CCNode::getAttributeInternal(std::string const& attr) {
auto meta = GeodeNodeMetadata::set(this);
if (meta->m_attributes.count(attr)) {
return meta->m_attributes.at(attr);
@ -177,4 +193,44 @@ std::optional<std::any> CCNode::getAttributeInternal(std::string const& attr) {
return std::nullopt;
}
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);
}
}
void CCNode::removeEventListener(EventListenerProtocol* listener) {
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;
}
#pragma warning(pop)

View file

@ -1,13 +1,14 @@
#include <Geode/loader/Event.hpp>
#include <Geode/utils/ranges.hpp>
using namespace geode::prelude;
void EventListenerProtocol::enable() {
Event::listeners().insert(this);
Event::listeners().push_back(this);
}
void EventListenerProtocol::disable() {
Event::listeners().erase(this);
ranges::remove(Event::listeners(), this);
}
EventListenerProtocol::~EventListenerProtocol() {
@ -19,7 +20,7 @@ Event::~Event() {}
void Event::postFrom(Mod* m) {
if (m) this->sender = m;
std::unordered_set<EventListenerProtocol*> listeners_copy = Event::listeners();
std::vector<EventListenerProtocol*> listeners_copy = Event::listeners();
for (auto h : listeners_copy) {
if (h->passThrough(this) == ListenerResult::Stop) {
@ -28,7 +29,11 @@ void Event::postFrom(Mod* m) {
}
}
std::unordered_set<EventListenerProtocol*>& Event::listeners() {
static std::unordered_set<EventListenerProtocol*> listeners;
std::vector<EventListenerProtocol*>& Event::listeners() {
static std::vector<EventListenerProtocol*> listeners;
return listeners;
}
void Event::prioritize(EventListenerProtocol* listener) {
ranges::move(Event::listeners(), listener, 0);
}

View file

@ -73,7 +73,7 @@ ghc::filesystem::path Mod::getBinaryPath() const {
}
ghc::filesystem::path Mod::getResourcesDir() const {
return dirs::getModRuntimeDir() / this->getID() / "resources";
return dirs::getModRuntimeDir() / this->getID() / "resources" / this->getID();
}
Result<> Mod::saveData() {

View file

@ -303,6 +303,10 @@ DECL_BASED_CREATE_FUNS(Leaderboard);
DECL_BASED_CREATE_FUNS(Editor);
DECL_BASED_CREATE_FUNS(Category);
CCSize CircleButtonSprite::getMaxTopSize() const {
return m_obContentSize * .65f;
}
CCSize EditorButtonSprite::getMaxTopSize() const {
return m_obContentSize - CCSize { 8.f, 8.f };
}

View file

@ -6,8 +6,6 @@ using namespace geode::prelude;
bool Scrollbar::ccTouchBegan(CCTouch* touch, CCEvent* event) {
// hitbox
auto rect = this->boundingBox();
// since anchor point is 0.5, 0.5 it's offset
rect.origin -= this->getScaledContentSize() / 2;
if (!m_target || !rect.containsPoint(touch->getLocation())) return false;
// trigger scrollbar thumb move
@ -40,7 +38,7 @@ void Scrollbar::ccTouchMoved(CCTouch* touch, CCEvent*) {
auto thumbHeight = m_resizeThumb ? std::min(p, 1.f) * targetHeight / .4f : 0;
auto posY = h *
((-pos.y - targetHeight / 2 + thumbHeight / 4 - 5) / (targetHeight - thumbHeight / 2 + 10));
((-pos.y + thumbHeight / 4 - 5) / (targetHeight - thumbHeight / 2 + 10));
if (posY > 0.0f) posY = 0.0f;
if (posY < -h) posY = -h;
@ -73,7 +71,7 @@ void Scrollbar::draw() {
m_track->setContentSize({ m_width / m_track->getScale(),
targetHeight / m_track->getScale() });
}
m_track->setPosition(.0f, .0f);
m_track->setPosition(m_obContentSize / 2);
this->setContentSize({ m_width, targetHeight });
@ -113,15 +111,21 @@ void Scrollbar::draw() {
if (fHeightTop() > 0.0f) {
thumbHeight -= fHeightTop();
if (thumbHeight < 15.f) {
thumbHeight = 15.f;
}
thumbPosY -= fHeightTop();
}
if (fHeightBottom() < 0.f) {
thumbHeight += fHeightBottom();
if (thumbHeight < 15.f) {
thumbHeight = 15.f;
}
thumbPosY -= fHeightBottom();
}
m_thumb->setPosition(0.f, thumbPosY);
m_thumb->setPosition(m_obContentSize / 2 + ccp(0.f, thumbPosY));
if (m_resizeThumb) {
m_thumb->setContentSize({ m_width, thumbHeight });
}
@ -134,6 +138,8 @@ void Scrollbar::setTarget(CCScrollLayerExt* target) {
bool Scrollbar::init(CCScrollLayerExt* target) {
if (!this->CCLayer::init()) return false;
this->ignoreAnchorPointForPosition(false);
m_target = target;
if (cocos::fileExistsInSearchPaths("scrollbar.png"_spr)) {

View file

@ -233,6 +233,32 @@ std::string geode::cocos::cc4bToHexString(ccColor4B const& color) {
return output;
}
WeakRefPool* WeakRefPool::get() {
static auto inst = new WeakRefPool();
return inst;
}
void WeakRefPool::check(CCObject* obj) {
// if this object's only reference is the WeakRefPool aka only weak
// references exist to it, then release it
if (m_pool.contains(obj) && obj->retainCount() == 1) {
obj->release();
m_pool.erase(obj);
}
}
bool WeakRefPool::isManaged(CCObject* obj) {
this->check(obj);
return m_pool.contains(obj);
}
void WeakRefPool::manage(CCObject* obj) {
if (obj && !m_pool.contains(obj)) {
obj->retain();
m_pool.insert(obj);
}
}
CCRect geode::cocos::calculateNodeCoverage(std::vector<CCNode*> const& nodes) {
CCRect coverage;
for (auto child : nodes) {

View file

@ -17,6 +17,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE
target_compile_definitions(${PROJECT_NAME} PRIVATE -DGEODE_DONT_WARN_INCORRECT_MEMBERS)
target_link_libraries(${PROJECT_NAME} PRIVATE ghc_filesystem)
target_link_libraries(${PROJECT_NAME} PRIVATE ghc_filesystem mat-json)
add_dependencies(${PROJECT_NAME} CodegenRun)