mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-26 17:36:05 -05:00
fix ranges::reverse + add break line option to AxisLayoutOptions
also EditorPauseLayer ids but didn't finish the layouts yet
This commit is contained in:
parent
f7ddf0af2e
commit
ffd50eb0f1
7 changed files with 251 additions and 128 deletions
10
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
10
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
|
@ -616,17 +616,15 @@ public:
|
|||
* Return an array of children
|
||||
*
|
||||
* Composing a "tree" structure is a very important feature of CCNode
|
||||
* Here's a sample code of traversing children array:
|
||||
* @code
|
||||
* @example
|
||||
* // Here's a sample code of traversing children array:
|
||||
* CCNode* node = NULL;
|
||||
* CCARRAY_FOREACH(parent->getChildren(), node)
|
||||
* {
|
||||
* node->setPosition(0,0);
|
||||
* }
|
||||
* @endcode
|
||||
* This sample code traverses all children nodes, and set theie position to (0,0)
|
||||
*
|
||||
* @return An array of children
|
||||
* // This sample code traverses all children nodes, and set theie position to (0,0)
|
||||
* @returns An array of children
|
||||
*/
|
||||
virtual CCArray* getChildren();
|
||||
|
||||
|
|
12
loader/include/Geode/cocos/base_nodes/Layout.hpp
vendored
12
loader/include/Geode/cocos/base_nodes/Layout.hpp
vendored
|
@ -81,12 +81,14 @@ class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
|||
protected:
|
||||
std::optional<float> m_maxScale = 1.f;
|
||||
std::optional<float> m_length = std::nullopt;
|
||||
bool m_breakLine = false;
|
||||
|
||||
public:
|
||||
static AxisLayoutOptions* create();
|
||||
|
||||
std::optional<float> getMaxScale();
|
||||
std::optional<float> getLength();
|
||||
std::optional<float> getMaxScale() const;
|
||||
std::optional<float> getLength() const;
|
||||
bool getBreakLine() const;
|
||||
|
||||
/**
|
||||
* Set the maximum scale this node can be if it's contained in an
|
||||
|
@ -106,6 +108,12 @@ public:
|
|||
* dynamically calculated based on content size
|
||||
*/
|
||||
AxisLayoutOptions* setLength(std::optional<float> length);
|
||||
|
||||
/**
|
||||
* If enabled, the node will always cause a growable axis layout to break
|
||||
* into a new line even if the current line could've fit the next node
|
||||
*/
|
||||
AxisLayoutOptions* setBreakLine(bool enable);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -563,86 +563,6 @@ namespace geode::cocos {
|
|||
*/
|
||||
GEODE_DLL bool fileExistsInSearchPaths(char const* filename);
|
||||
|
||||
template <typename T>
|
||||
struct CCArrayIterator {
|
||||
public:
|
||||
CCArrayIterator(T* p) : m_ptr(p) {}
|
||||
|
||||
T* m_ptr;
|
||||
|
||||
auto& operator*() {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
auto& operator*() const {
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
auto operator->() {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
auto operator->() const {
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
auto& operator++() {
|
||||
++m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto& operator--() {
|
||||
--m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto& operator+=(size_t val) {
|
||||
m_ptr += val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto& operator-=(size_t val) {
|
||||
m_ptr -= val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator+(size_t val) const {
|
||||
return CCArrayIterator<T>(m_ptr + val);
|
||||
}
|
||||
|
||||
auto operator-(size_t val) const {
|
||||
return CCArrayIterator<T>(m_ptr - val);
|
||||
}
|
||||
|
||||
auto operator-(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr - other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator<(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr < other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator>(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr > other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator<=(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr <= other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator>=(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr >= other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator==(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr == other.m_ptr;
|
||||
}
|
||||
|
||||
bool operator!=(CCArrayIterator<T> const& other) const {
|
||||
return m_ptr != other.m_ptr;
|
||||
}
|
||||
};
|
||||
|
||||
inline void ccDrawColor4B(cocos2d::ccColor4B const& color) {
|
||||
cocos2d::ccDrawColor4B(color.r, color.g, color.b, color.a);
|
||||
}
|
||||
|
@ -781,16 +701,6 @@ namespace std {
|
|||
return std::hash<T*>()(ref.data());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct iterator_traits<geode::cocos::CCArrayIterator<T>> {
|
||||
using difference_type = ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
using iterator_category =
|
||||
std::random_access_iterator_tag; // its random access but im too lazy to implement it
|
||||
};
|
||||
}
|
||||
|
||||
// more utils
|
||||
|
@ -822,9 +732,14 @@ namespace geode::cocos {
|
|||
using T = std::remove_pointer_t<_Type>;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
using iterator = T**;
|
||||
using const_iterator = const T**;
|
||||
|
||||
CCArrayExt() : m_arr(cocos2d::CCArray::create()) {}
|
||||
|
||||
CCArrayExt(cocos2d::CCArray* arr) : m_arr(arr) {}
|
||||
CCArrayExt(cocos2d::CCArray* arr)
|
||||
: m_arr(arr) {}
|
||||
|
||||
CCArrayExt(CCArrayExt const& a) : m_arr(a.m_arr) {}
|
||||
|
||||
|
@ -834,18 +749,26 @@ namespace geode::cocos {
|
|||
|
||||
~CCArrayExt() {}
|
||||
|
||||
auto begin() {
|
||||
T** begin() const {
|
||||
if (!m_arr) {
|
||||
return CCArrayIterator<T*>(nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
return CCArrayIterator<T*>(reinterpret_cast<T**>(m_arr->data->arr));
|
||||
return reinterpret_cast<T**>(m_arr->data->arr);
|
||||
}
|
||||
|
||||
auto end() {
|
||||
T** end() const {
|
||||
if (!m_arr) {
|
||||
return CCArrayIterator<T*>(nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
return CCArrayIterator<T*>(reinterpret_cast<T**>(m_arr->data->arr) + m_arr->count());
|
||||
return reinterpret_cast<T**>(m_arr->data->arr) + m_arr->count();
|
||||
}
|
||||
|
||||
auto rbegin() const {
|
||||
return std::reverse_iterator(this->end());
|
||||
}
|
||||
|
||||
auto rend() const {
|
||||
return std::reverse_iterator(this->begin());
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
|
|
|
@ -289,23 +289,21 @@ namespace geode::utils::ranges {
|
|||
return member(*it);
|
||||
}
|
||||
|
||||
template <ValidConstContainer C>
|
||||
struct ConstReverseWrapper {
|
||||
C const& iter;
|
||||
template <class C>
|
||||
struct ReverseWrapper {
|
||||
C iter;
|
||||
|
||||
decltype(auto) begin() {
|
||||
return std::rbegin(iter);
|
||||
}
|
||||
|
||||
decltype(auto) end() {
|
||||
return std::rend(iter);
|
||||
}
|
||||
};
|
||||
|
||||
template <ValidConstContainer C>
|
||||
auto begin(ConstReverseWrapper<C> const& c) {
|
||||
return std::rbegin(c.iter);
|
||||
}
|
||||
|
||||
template <ValidConstContainer C>
|
||||
auto end(ConstReverseWrapper<C> const& c) {
|
||||
return std::rend(c.iter);
|
||||
}
|
||||
|
||||
template <ValidConstContainer C>
|
||||
ConstReverseWrapper<C> reverse(C const& iter) {
|
||||
return { iter };
|
||||
template <class C>
|
||||
auto reverse(C&& iter) {
|
||||
return ReverseWrapper<C>{std::forward<C>(iter)};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
|
@ -40,6 +41,18 @@ CCArray* Layout::getNodesToPosition(CCNode* on) {
|
|||
return filtered;
|
||||
}
|
||||
|
||||
static AxisLayoutOptions* axisOpts(CCNode* node) {
|
||||
if (!node) return nullptr;
|
||||
return typeinfo_cast<AxisLayoutOptions*>(node->getLayoutOptions());
|
||||
}
|
||||
|
||||
static bool isOptsBreakLine(CCNode* node) {
|
||||
if (auto opts = axisOpts(node)) {
|
||||
return opts->getBreakLine();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr float AXIS_MIN_SCALE = 0.65f;
|
||||
|
||||
struct AxisPosition {
|
||||
|
@ -52,9 +65,13 @@ struct AxisPosition {
|
|||
static AxisPosition nodeAxis(CCNode* node, Axis axis, float scale) {
|
||||
auto scaledSize = node->getScaledContentSize() * scale;
|
||||
std::optional<float> axisLength = std::nullopt;
|
||||
if (auto opts = typeinfo_cast<AxisLayoutOptions*>(node->getLayoutOptions())) {
|
||||
if (auto opts = axisOpts(node)) {
|
||||
axisLength = opts->getLength();
|
||||
}
|
||||
// CCMenuItemToggler is a common quirky class
|
||||
if (auto toggle = typeinfo_cast<CCMenuItemToggler*>(node)) {
|
||||
scaledSize = toggle->m_offButton->getScaledContentSize();
|
||||
}
|
||||
auto anchor = node->getAnchorPoint();
|
||||
if (axis == Axis::Row) {
|
||||
return AxisPosition {
|
||||
|
@ -104,10 +121,11 @@ AxisLayout::Row* AxisLayout::fitInRow(CCNode* on, CCArray* nodes, float scale, f
|
|||
auto res = CCArray::create();
|
||||
|
||||
auto available = nodeAxis(on, m_axis, 1.f);
|
||||
CCNode* prev = nullptr;
|
||||
size_t ix = 0;
|
||||
for (auto& node : CCArrayExt<CCNode*>(nodes)) {
|
||||
if (m_autoScale) {
|
||||
if (auto opts = typeinfo_cast<AxisLayoutOptions*>(node->getLayoutOptions())) {
|
||||
if (auto opts = axisOpts(node)) {
|
||||
if (auto max = opts->getMaxScale()) {
|
||||
node->setScale(max.value());
|
||||
}
|
||||
|
@ -122,7 +140,12 @@ AxisLayout::Row* AxisLayout::fitInRow(CCNode* on, CCArray* nodes, float scale, f
|
|||
// next row
|
||||
// also force at least one object to be added to this row, because if
|
||||
// it's too large for this row it's gonna be too large for all rows
|
||||
if (m_growCrossAxis && nextAxisLength > available.axisLength && ix != 0) {
|
||||
if (
|
||||
m_growCrossAxis && (
|
||||
(nextAxisLength > available.axisLength || isOptsBreakLine(prev)) &&
|
||||
ix != 0
|
||||
)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
res->addObject(node);
|
||||
|
@ -134,6 +157,7 @@ AxisLayout::Row* AxisLayout::fitInRow(CCNode* on, CCArray* nodes, float scale, f
|
|||
if (pos.crossLength > crossLength) {
|
||||
crossLength = pos.crossLength;
|
||||
}
|
||||
prev = node;
|
||||
ix++;
|
||||
}
|
||||
|
||||
|
@ -502,14 +526,18 @@ AxisLayoutOptions* AxisLayoutOptions::create() {
|
|||
return new AxisLayoutOptions();
|
||||
}
|
||||
|
||||
std::optional<float> AxisLayoutOptions::getMaxScale() {
|
||||
std::optional<float> AxisLayoutOptions::getMaxScale() const {
|
||||
return m_maxScale;
|
||||
}
|
||||
|
||||
std::optional<float> AxisLayoutOptions::getLength() {
|
||||
std::optional<float> AxisLayoutOptions::getLength() const {
|
||||
return m_length;
|
||||
}
|
||||
|
||||
bool AxisLayoutOptions::getBreakLine() const {
|
||||
return m_breakLine;
|
||||
}
|
||||
|
||||
AxisLayoutOptions* AxisLayoutOptions::setMaxScale(float scale) {
|
||||
m_maxScale = scale;
|
||||
return this;
|
||||
|
@ -524,3 +552,8 @@ AxisLayoutOptions* AxisLayoutOptions::setLength(std::optional<float> length) {
|
|||
m_length = length;
|
||||
return this;
|
||||
}
|
||||
|
||||
AxisLayoutOptions* AxisLayoutOptions::setBreakLine(bool enable) {
|
||||
m_breakLine = enable;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -81,10 +81,10 @@ static CCMenu* detachAndCreateMenu(CCNode* parent, const char* menuID, Layout* l
|
|||
first->setZOrder(0);
|
||||
newMenu->addChild(first);
|
||||
first->release();
|
||||
|
||||
newMenu->setLayout(layout);
|
||||
|
||||
(switchToMenu(args, newMenu), ...);
|
||||
|
||||
newMenu->setLayout(layout);
|
||||
|
||||
return newMenu;
|
||||
}
|
||||
|
|
163
loader/src/ids/EditorPauseLayer.cpp
Normal file
163
loader/src/ids/EditorPauseLayer.cpp
Normal file
|
@ -0,0 +1,163 @@
|
|||
#include "AddIDs.hpp"
|
||||
|
||||
#include <Geode/modify/EditorPauseLayer.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
$register_ids(EditorPauseLayer) {
|
||||
auto winSize = CCDirector::get()->getWinSize();
|
||||
|
||||
if (auto menu = getChildOfType<CCMenu>(this, 0)) {
|
||||
menu->setID("resume-menu");
|
||||
|
||||
setIDs(
|
||||
menu, 0,
|
||||
"resume-button",
|
||||
"save-and-play-button",
|
||||
"save-and-exit-button",
|
||||
"save-button",
|
||||
"exit-button"
|
||||
);
|
||||
|
||||
menu->setContentSize({ 100.f, 220.f });
|
||||
menu->setLayout(
|
||||
ColumnLayout::create()
|
||||
->setGap(10.f)
|
||||
->setAxisReverse(true)
|
||||
);
|
||||
}
|
||||
|
||||
setIDs(
|
||||
this, 2,
|
||||
"ignore-damage-label",
|
||||
"follow-player-label",
|
||||
"select-filter-label",
|
||||
"show-grid-label",
|
||||
"show-object-info-label",
|
||||
"show-ground-label",
|
||||
"preview-mode-label",
|
||||
|
||||
"object-count-label",
|
||||
"length-label",
|
||||
"length-name-label"
|
||||
);
|
||||
|
||||
if (auto menu = getChildOfType<CCMenu>(this, 1)) {
|
||||
menu->setID("bottom-menu");
|
||||
|
||||
setIDs(
|
||||
menu, 0,
|
||||
"guidelines-enable-button",
|
||||
"help-button",
|
||||
"guidelines-disable-button",
|
||||
|
||||
"uncheck-portals-button",
|
||||
"reset-unused-button",
|
||||
"create-edges-button",
|
||||
"create-outlines-button",
|
||||
"create-base-button",
|
||||
"build-helper-button",
|
||||
|
||||
"align-x-button",
|
||||
"align-y-button",
|
||||
"select-all-button",
|
||||
"select-all-left-button",
|
||||
"select-all-right-button",
|
||||
|
||||
"ignore-damage-toggle",
|
||||
"follow-player-toggle",
|
||||
"select-filter-toggle",
|
||||
"show-grid-toggle",
|
||||
"show-object-info-toggle",
|
||||
"show-ground-toggle",
|
||||
"preview-mode-toggle",
|
||||
|
||||
"keys-button",
|
||||
"settings-button"
|
||||
);
|
||||
|
||||
auto smallActionsMenu = detachAndCreateMenu(
|
||||
this,
|
||||
"small-actions-menu",
|
||||
ColumnLayout::create(),
|
||||
menu->getChildByID("align-x-button"),
|
||||
menu->getChildByID("align-y-button"),
|
||||
menu->getChildByID("select-all-button"),
|
||||
menu->getChildByID("select-all-left-button"),
|
||||
menu->getChildByID("select-all-right-button")
|
||||
);
|
||||
|
||||
auto actionsMenu = detachAndCreateMenu(
|
||||
this,
|
||||
"actions-menu",
|
||||
ColumnLayout::create(),
|
||||
menu->getChildByID("uncheck-portals-button"),
|
||||
menu->getChildByID("reset-unused-button"),
|
||||
menu->getChildByID("create-edges-button"),
|
||||
menu->getChildByID("create-outlines-button"),
|
||||
menu->getChildByID("create-base-button"),
|
||||
menu->getChildByID("build-helper-button"),
|
||||
menu->getChildByID("keys-button")
|
||||
);
|
||||
|
||||
auto optionsMenu = detachAndCreateMenu(
|
||||
this,
|
||||
"options-menu",
|
||||
ColumnLayout::create()
|
||||
->setGap(-1.f)
|
||||
->setAxisAlignment(AxisAlignment::Start)
|
||||
->setGrowCrossAxis(true)
|
||||
->setCrossAxisAlignment(AxisAlignment::Start)
|
||||
->setCrossAxisOverflow(false),
|
||||
menu->getChildByID("ignore-damage-toggle"),
|
||||
this->getChildByID("ignore-damage-label"),
|
||||
menu->getChildByID("follow-player-toggle"),
|
||||
this->getChildByID("follow-player-label"),
|
||||
menu->getChildByID("select-filter-toggle"),
|
||||
this->getChildByID("select-filter-label"),
|
||||
menu->getChildByID("show-grid-toggle"),
|
||||
this->getChildByID("show-grid-label"),
|
||||
menu->getChildByID("show-object-info-toggle"),
|
||||
this->getChildByID("show-object-info-label"),
|
||||
menu->getChildByID("show-ground-toggle"),
|
||||
this->getChildByID("show-ground-label"),
|
||||
menu->getChildByID("preview-mode-toggle"),
|
||||
this->getChildByID("preview-mode-label")
|
||||
);
|
||||
for (auto node : CCArrayExt<CCNode>(optionsMenu->getChildren())) {
|
||||
if (auto label = typeinfo_cast<CCLabelBMFont*>(node)) {
|
||||
label->setLayoutOptions(
|
||||
AxisLayoutOptions::create()
|
||||
->setBreakLine(true)
|
||||
);
|
||||
}
|
||||
}
|
||||
optionsMenu->setContentSize({ 120.f, winSize.height - 100.f });
|
||||
optionsMenu->setPosition(70.f, winSize.height / 2 - 50.f + 10.f);
|
||||
optionsMenu->updateLayout();
|
||||
|
||||
auto settingsMenu = detachAndCreateMenu(
|
||||
this,
|
||||
"settings-menu",
|
||||
RowLayout::create()
|
||||
->setAxisReverse(true),
|
||||
menu->getChildByID("settings-button")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct EditorPauseLayerIDs : Modify<EditorPauseLayerIDs, EditorPauseLayer> {
|
||||
static void onModify(auto& self) {
|
||||
if (!self.setHookPriority("EditorPauseLayer::init", GEODE_ID_PRIORITY)) {
|
||||
log::warn("Failed to set EditorPauseLayer::init hook priority, node IDs may not work properly");
|
||||
}
|
||||
}
|
||||
|
||||
bool init(LevelEditorLayer* lel) {
|
||||
if (!EditorPauseLayer::init(lel)) return false;
|
||||
|
||||
NodeIDs::get()->provide(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue