Merge branch 'main' into new-index-but-better

This commit is contained in:
Fleeym 2024-05-15 23:58:55 +03:00
commit 89273c9458
15 changed files with 309 additions and 25 deletions

View file

@ -1,5 +1,17 @@
# Geode Changelog
## v2.0.0-beta.26
* Bring in several UI helpers from the `new-index-but-better` branch: `ListBorders`, `addSideArt`, `AxisLayout` improvements, ... (26729c3, 7ff257c)
* Make it possible to compile mods in Debug mode (517ad45)
* Add `GJDifficultyName` and `GJFeatureState` (#706)
* Add `geode::cocos::isSpriteName` and `geode::cocos::getChildBySpriteName` (#725)
* Add some Android keycodes (4fa8098)
* Update FMOD on Mac (c4a682b)
* Bump JSON version (5cc23e7)
* Fixes to `InputNode` touches (29b4732)
* Fix `file::readFromJson` (77e0f2e)
* Fix issues with TulipHook (f2ce7d0)
## v2.0.0-beta.25
* Fix updater sometimes skipping releases (18dd0b7)
* Fix resources getting downloaded every time (5f571d9)

View file

@ -1 +1 @@
2.0.0-beta.25
2.0.0-beta.26

View file

@ -913,6 +913,22 @@ public:
*/
GEODE_DLL CCNode* getChildByIDRecursive(std::string const& id);
/**
* Get a child based on a query. Searches the child tree for a matching
* child. The query currently only supports the following features:
* - `node-id`: Match a node with a specific ID
* - `node-id-1 node-id-2`: Match a descendant (possibly not immediate)
* child of a node with a specific ID
* - `node-id-1 > node-id-2`: Match the immediate child of a node with a
* specific ID
* For example, the query "my-layer button-menu > mod.id/epic-button" is
* equivalent to `getChildByIDRecursive("my-layer")
* ->getChildByIDRecursive("button-menu")
* ->getChildByID("mod.id/epic-button")`
* @returns The first matching node, or nullptr if none was found
*/
GEODE_DLL CCNode* querySelector(std::string const& query);
/**
* Removes a child from the container by its ID.
* @param id The ID of the node

View file

@ -5,7 +5,7 @@
#include "../cocoa/CCArray.h"
#include <Geode/platform/platform.hpp>
#include <optional>
#include <unordered_map>
#include <memory>
NS_CC_BEGIN

View file

@ -313,7 +313,31 @@ public:
virtual bool isBlendAdditive();
virtual void setBlendAdditive(bool value);
//////////////////////////////////////////////////////////////////////////
RT_ADD(
float m_fFadeInTime;
float m_fFadeInTimeVar;
float m_fFadeOutTime;
float m_fFadeOutTimeVar;
float m_fFrictionPos;
float m_fFrictionPosVar;
float m_fFrictionSize;
float m_fFrictionSizeVar;
float m_fFrictionRot;
float m_fFrictionRotVar;
float m_fRespawn;
float m_fRespawnVar;
bool m_bStartSpinEqualToEnd;
bool m_bStartSizeEqualToEnd;
bool m_bStartRadiusEqualToEnd;
bool m_bDynamicRotationIsDir;
bool m_bOrderSensitive;
bool m_bStartRGBVarSync;
bool m_bEndRGBVarSync;
bool m_bWasRemoved;
bool m_bUsingSchedule;
)
/** start size in pixels of each particle */
CC_PROPERTY(float, m_fStartSize, StartSize)
/** size variance in pixels of each particle */

View file

@ -5,7 +5,7 @@
#include "CCStdC.h"
#include "../CCCommon.h"
#include "../CCApplicationProtocol.h"
#include "CCControllerHandler.h"
#include "CXBOXController.h"
#include <string>
NS_CC_BEGIN
@ -51,7 +51,7 @@ public:
RT_ADD(
void setupVerticalSync();
void updateVerticalSync();
void updateControllerKeys();
void updateControllerKeys(CXBOXController* controller, int userIndex);
int getTimeElapsed();
void resetForceTimer();
@ -96,8 +96,8 @@ public:
LARGE_INTEGER m_nVsyncInterval;
gd::string m_resourceRootPath;
gd::string m_startupScriptFilename;
CCControllerHandler* m_pControllerHandler;
void* m_unk; //might be swapped with m_pControllerHandler
CXBOXController* m_pControllerHandler;
CXBOXController* m_pController2Handler; //might be swapped with m_pControllerHandler
bool m_bUpdateController;
CC_SYNTHESIZE_NV(bool, m_bShutdownCalled, ShutdownCalled);
INPUT m_iInput;
@ -114,6 +114,7 @@ public:
CC_SYNTHESIZE_NV(bool, m_bFullscreen, Fullscreen);
CC_SYNTHESIZE_NV(bool, m_bBorderless, Borderless);
protected:
static CCApplication * sm_pSharedApplication;
};

View file

@ -208,7 +208,9 @@ public:
float m_fMouseX;
float m_fMouseY;
bool m_bIsFullscreen;
bool m_bIsBorderless;
bool m_bShouldHideCursor;
bool m_bCursorLocked;
bool m_bShouldCallGLFinish;
)

View file

@ -1,14 +1,12 @@
#ifndef __CC_CONTROLLER_HANDLER_WIN32_H__
#define __CC_CONTROLLER_HANDLER_WIN32_H__
#ifndef __CXBOXCONTROLLER_WIN32_H__
#define __CXBOXCONTROLLER_WIN32_H__
#include "../../include/ccMacros.h"
#include "CCStdC.h"
#include "CCControllerState.h"
#include <Xinput.h>
NS_CC_BEGIN
class CC_DLL CCControllerHandler
class CC_DLL CXBOXController
{
GEODE_FRIEND_MODIFY
public:
@ -35,6 +33,4 @@ public:
bool m_buttonY;
};
NS_CC_END
#endif
#endif

View file

@ -27,6 +27,13 @@ namespace geode {
return static_cast<bool>(static_cast<int>(a) & static_cast<int>(b));
}
enum class SideArtStyle {
Layer,
LayerGray,
PopupBlue,
PopupGold,
};
/**
* Add side art (corner pieces) for a layer
* @param to Layer to add corner pieces to
@ -34,7 +41,25 @@ namespace geode {
* @param useAnchorLayout If true, `to` is given an `AnchorLayout` and the
* corners' positions are dynamically updated
*/
GEODE_DLL void addSideArt(cocos2d::CCNode* to, SideArt sides = SideArt::All, bool useAnchorLayout = false);
GEODE_DLL void addSideArt(
cocos2d::CCNode* to,
SideArt sides = SideArt::All,
bool useAnchorLayout = false
);
/**
* Add side art (corner pieces) for a layer
* @param to Layer to add corner pieces to
* @param sides Which corners to populate; by default, populates all
* @param style Which side art sprites to use
* @param useAnchorLayout If true, `to` is given an `AnchorLayout` and the
* corners' positions are dynamically updated
*/
GEODE_DLL void addSideArt(
cocos2d::CCNode* to,
SideArt sides,
SideArtStyle style,
bool useAnchorLayout = false
);
/**
* Add the rounded comment borders to a node

View file

@ -196,6 +196,13 @@ namespace geode {
return this->Base::value_or(std::forward<U>(val));
}
[[nodiscard]] constexpr decltype(auto) unwrapOrDefault() && requires std::is_default_constructible_v<T> {
return this->Base::value_or(T());
}
[[nodiscard]] constexpr decltype(auto) unwrapOrDefault() const& requires std::is_default_constructible_v<T> {
return this->Base::value_or(T());
}
template <class U>
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) && {
return this->Base::error_or(std::forward<U>(val));

View file

@ -153,6 +153,13 @@ namespace geode {
}
GEODE_DLL std::string timePointAsString(std::chrono::system_clock::time_point const& tp);
/**
* Gets the display pixel factor for the current screen,
* i.e. the ratio between physical pixels and logical pixels on one axis.
* On most platforms this is 1.0, but on retina displays for example this returns 2.0.
*/
GEODE_DLL float getDisplayFactor();
}
}

View file

@ -3,6 +3,7 @@
#include <Geode/modify/Field.hpp>
#include <Geode/modify/CCNode.hpp>
#include <cocos2d.h>
#include <queue>
using namespace geode::prelude;
using namespace geode::modifier;
@ -143,6 +144,162 @@ CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
return nullptr;
}
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);
}
void CCNode::removeChildByID(std::string const& id) {
if (auto child = this->getChildByID(id)) {
this->removeChild(child);

View file

@ -9,6 +9,10 @@ using namespace geode::prelude;
#include <objc/runtime.h>
#include <Geode/utils/web.hpp>
#define CommentType CommentTypeDummy
#import <Cocoa/Cocoa.h>
#undef CommentType
bool utils::clipboard::write(std::string const& data) {
[[NSPasteboard generalPasteboard] clearContents];
[[NSPasteboard generalPasteboard] setString:[NSString stringWithUTF8String:data.c_str()]
@ -332,3 +336,16 @@ std::string geode::utils::thread::getDefaultName() {
void geode::utils::thread::platformSetName(std::string const& name) {
pthread_setname_np(name.c_str());
}
float geode::utils::getDisplayFactor() {
float displayScale = 1.f;
if ([[NSScreen mainScreen] respondsToSelector:@selector(backingScaleFactor)]) {
NSArray* screens = [NSScreen screens];
for (int i = 0; i < screens.count; i++) {
float s = [screens[i] backingScaleFactor];
if (s > displayScale)
displayScale = s;
}
}
return displayScale;
}

View file

@ -18,28 +18,40 @@ CCSprite* geode::createLayerBG() {
return bg;
}
void geode::addSideArt(CCNode* to, SideArt sides, bool useAnchorLayout) {
void geode::addSideArt(CCNode* to, SideArt sides, SideArtStyle style, bool useAnchorLayout) {
const char* sprite;
float offset;
switch (style) {
default:
case SideArtStyle::Layer: sprite = "GJ_sideArt_001.png"; offset = 35; break;
case SideArtStyle::LayerGray: sprite = "gauntletCorner_001.png"; offset = 35; break;
case SideArtStyle::PopupBlue: sprite = "rewardCorner_001.png"; offset = 24.75f; break;
case SideArtStyle::PopupGold: sprite = "dailyLevelCorner_001.png"; offset = 24.75f; break;
}
if (sides & SideArt::BottomLeft) {
auto spr = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png");
to->addChildAtPosition(spr, Anchor::BottomLeft, ccp(35, 35), useAnchorLayout);
auto spr = CCSprite::createWithSpriteFrameName(sprite);
to->addChildAtPosition(spr, Anchor::BottomLeft, ccp(offset, offset), useAnchorLayout);
}
if (sides & SideArt::BottomRight) {
auto spr = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png");
auto spr = CCSprite::createWithSpriteFrameName(sprite);
spr->setFlipX(true);
to->addChildAtPosition(spr, Anchor::BottomRight, ccp(-35, 35), useAnchorLayout);
to->addChildAtPosition(spr, Anchor::BottomRight, ccp(-offset, offset), useAnchorLayout);
}
if (sides & SideArt::TopLeft) {
auto spr = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png");
auto spr = CCSprite::createWithSpriteFrameName(sprite);
spr->setFlipY(true);
to->addChildAtPosition(spr, Anchor::TopLeft, ccp(35, -35), useAnchorLayout);
to->addChildAtPosition(spr, Anchor::TopLeft, ccp(offset, -offset), useAnchorLayout);
}
if (sides & SideArt::TopRight) {
auto spr = CCSprite::createWithSpriteFrameName("GJ_sideArt_001.png");
auto spr = CCSprite::createWithSpriteFrameName(sprite);
spr->setFlipX(true);
spr->setFlipY(true);
to->addChildAtPosition(spr, Anchor::TopRight, ccp(-35, -35), useAnchorLayout);
to->addChildAtPosition(spr, Anchor::TopRight, ccp(-offset, -offset), useAnchorLayout);
}
}
void geode::addSideArt(CCNode* to, SideArt sides, bool useAnchorLayout) {
return addSideArt(to, sides, SideArtStyle::Layer, useAnchorLayout);
}
void geode::addListBorders(CCNode* to, CCPoint const& center, CCSize const& size) {
// if the size is 346.f, the top aligns perfectly by default :3

View file

@ -0,0 +1,8 @@
#include <Geode/utils/general.hpp>
#ifndef GEODE_IS_MACOS
// feel free to properly implement this for other platforms
float geode::utils::getDisplayFactor() {
return 1.0f;
}
#endif