From ffd50eb0f19f58930597ad8ec6efa7db3d242843 Mon Sep 17 00:00:00 2001 From: HJfod <60038575+HJfod@users.noreply.github.com> Date: Tue, 14 Feb 2023 21:54:45 +0200 Subject: [PATCH] fix ranges::reverse + add break line option to AxisLayoutOptions also EditorPauseLayer ids but didn't finish the layouts yet --- .../include/Geode/cocos/base_nodes/CCNode.h | 10 +- .../include/Geode/cocos/base_nodes/Layout.hpp | 12 +- loader/include/Geode/utils/cocos.hpp | 117 +++---------- loader/include/Geode/utils/ranges.hpp | 30 ++-- loader/src/cocos2d-ext/Layout.cpp | 43 ++++- loader/src/ids/AddIDs.hpp | 4 +- loader/src/ids/EditorPauseLayer.cpp | 163 ++++++++++++++++++ 7 files changed, 251 insertions(+), 128 deletions(-) create mode 100644 loader/src/ids/EditorPauseLayer.cpp diff --git a/loader/include/Geode/cocos/base_nodes/CCNode.h b/loader/include/Geode/cocos/base_nodes/CCNode.h index 6ab47d21..4c8225c0 100644 --- a/loader/include/Geode/cocos/base_nodes/CCNode.h +++ b/loader/include/Geode/cocos/base_nodes/CCNode.h @@ -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(); diff --git a/loader/include/Geode/cocos/base_nodes/Layout.hpp b/loader/include/Geode/cocos/base_nodes/Layout.hpp index 62371922..1256f482 100644 --- a/loader/include/Geode/cocos/base_nodes/Layout.hpp +++ b/loader/include/Geode/cocos/base_nodes/Layout.hpp @@ -81,12 +81,14 @@ class GEODE_DLL AxisLayoutOptions : public LayoutOptions { protected: std::optional m_maxScale = 1.f; std::optional m_length = std::nullopt; + bool m_breakLine = false; public: static AxisLayoutOptions* create(); - std::optional getMaxScale(); - std::optional getLength(); + std::optional getMaxScale() const; + std::optional 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 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); }; /** diff --git a/loader/include/Geode/utils/cocos.hpp b/loader/include/Geode/utils/cocos.hpp index e0f38990..c16280e7 100644 --- a/loader/include/Geode/utils/cocos.hpp +++ b/loader/include/Geode/utils/cocos.hpp @@ -563,86 +563,6 @@ namespace geode::cocos { */ GEODE_DLL bool fileExistsInSearchPaths(char const* filename); - template - 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(m_ptr + val); - } - - auto operator-(size_t val) const { - return CCArrayIterator(m_ptr - val); - } - - auto operator-(CCArrayIterator const& other) const { - return m_ptr - other.m_ptr; - } - - bool operator<(CCArrayIterator const& other) const { - return m_ptr < other.m_ptr; - } - - bool operator>(CCArrayIterator const& other) const { - return m_ptr > other.m_ptr; - } - - bool operator<=(CCArrayIterator const& other) const { - return m_ptr <= other.m_ptr; - } - - bool operator>=(CCArrayIterator const& other) const { - return m_ptr >= other.m_ptr; - } - - bool operator==(CCArrayIterator const& other) const { - return m_ptr == other.m_ptr; - } - - bool operator!=(CCArrayIterator 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()(ref.data()); } }; - - template - struct iterator_traits> { - 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(nullptr); + return nullptr; } - return CCArrayIterator(reinterpret_cast(m_arr->data->arr)); + return reinterpret_cast(m_arr->data->arr); } - auto end() { + T** end() const { if (!m_arr) { - return CCArrayIterator(nullptr); + return nullptr; } - return CCArrayIterator(reinterpret_cast(m_arr->data->arr) + m_arr->count()); + return reinterpret_cast(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 { diff --git a/loader/include/Geode/utils/ranges.hpp b/loader/include/Geode/utils/ranges.hpp index 0481285b..324abad4 100644 --- a/loader/include/Geode/utils/ranges.hpp +++ b/loader/include/Geode/utils/ranges.hpp @@ -289,23 +289,21 @@ namespace geode::utils::ranges { return member(*it); } - template - struct ConstReverseWrapper { - C const& iter; + template + struct ReverseWrapper { + C iter; + + decltype(auto) begin() { + return std::rbegin(iter); + } + + decltype(auto) end() { + return std::rend(iter); + } }; - template - auto begin(ConstReverseWrapper const& c) { - return std::rbegin(c.iter); - } - - template - auto end(ConstReverseWrapper const& c) { - return std::rend(c.iter); - } - - template - ConstReverseWrapper reverse(C const& iter) { - return { iter }; + template + auto reverse(C&& iter) { + return ReverseWrapper{std::forward(iter)}; } } diff --git a/loader/src/cocos2d-ext/Layout.cpp b/loader/src/cocos2d-ext/Layout.cpp index bd7661ac..12d3e444 100644 --- a/loader/src/cocos2d-ext/Layout.cpp +++ b/loader/src/cocos2d-ext/Layout.cpp @@ -3,6 +3,7 @@ #include #include #include +#include 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(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 axisLength = std::nullopt; - if (auto opts = typeinfo_cast(node->getLayoutOptions())) { + if (auto opts = axisOpts(node)) { axisLength = opts->getLength(); } + // CCMenuItemToggler is a common quirky class + if (auto toggle = typeinfo_cast(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(nodes)) { if (m_autoScale) { - if (auto opts = typeinfo_cast(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 AxisLayoutOptions::getMaxScale() { +std::optional AxisLayoutOptions::getMaxScale() const { return m_maxScale; } -std::optional AxisLayoutOptions::getLength() { +std::optional 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 length) { m_length = length; return this; } + +AxisLayoutOptions* AxisLayoutOptions::setBreakLine(bool enable) { + m_breakLine = enable; + return this; +} diff --git a/loader/src/ids/AddIDs.hpp b/loader/src/ids/AddIDs.hpp index 7a8c967f..77693e91 100644 --- a/loader/src/ids/AddIDs.hpp +++ b/loader/src/ids/AddIDs.hpp @@ -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; } diff --git a/loader/src/ids/EditorPauseLayer.cpp b/loader/src/ids/EditorPauseLayer.cpp new file mode 100644 index 00000000..e0fdfccb --- /dev/null +++ b/loader/src/ids/EditorPauseLayer.cpp @@ -0,0 +1,163 @@ +#include "AddIDs.hpp" + +#include + +USE_GEODE_NAMESPACE(); + +$register_ids(EditorPauseLayer) { + auto winSize = CCDirector::get()->getWinSize(); + + if (auto menu = getChildOfType(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(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(optionsMenu->getChildren())) { + if (auto label = typeinfo_cast(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 { + 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; + } +};