#pragma once #include "../include/ccMacros.h" #include "../cocoa/CCAffineTransform.h" #include "../cocoa/CCArray.h" #include <Geode/platform/platform.hpp> #include <optional> #include <memory> NS_CC_BEGIN class CCNode; #pragma warning(push) #pragma warning(disable: 4275) /** * Layouts automatically handle the positioning of nodes. Use CCNode::setLayout * to apply a layout to a node, and then use CCNode::updateLayout to apply * the layout's positioning. Geode comes with a few default layouts like * RowLayout, ColumnLayout, and GridLayout, but if you need a different kind * of layout you can inherit from the Layout class. */ class GEODE_DLL Layout : public CCObject { protected: CCArray* getNodesToPosition(CCNode* forNode) const; bool m_ignoreInvisibleChildren = false; public: /** * Automatically apply the layout's positioning on a set of nodes * @param on Node to apply the layout on. Position's the node's children * according to the layout. The content size of the node should be * respected as a boundary the layout shouldn't overflow. The node may be * rescaled to better fit its contents */ virtual void apply(CCNode* on) = 0; /** * Get how much space this layout would like to take up for a given target */ virtual CCSize getSizeHint(CCNode* on) const = 0; void ignoreInvisibleChildren(bool ignore); bool isIgnoreInvisibleChildren() const; virtual ~Layout() = default; }; class GEODE_DLL LayoutOptions : public CCObject { public: virtual ~LayoutOptions() = default; }; /** * The direction of an AxisLayout */ enum class Axis { Row, Column, }; /** * Specifies the alignment of something in an AxisLayout */ enum class AxisAlignment { // Align items to the start // |ooo......| Start, // All items are centered // |...ooo...| Center, // Align items to the end // |......ooo| End, // Each item gets the same portion from the layout (disregards gap) // |.o..o..o.| Even, }; constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f; constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0; /** * Options for controlling the behaviour of individual nodes in an AxisLayout * @example * auto node = CCNode::create(); * // this node will have 10 units of spacing between it and the next one * node->setLayoutOptions( * AxisLayoutOptions::create() * ->setNextGap(10.f) * ); * someNodeWithALayout->addChild(node); */ class GEODE_DLL AxisLayoutOptions : public LayoutOptions { protected: class Impl; std::unique_ptr<Impl> m_impl; AxisLayoutOptions(); public: static AxisLayoutOptions* create(); virtual ~AxisLayoutOptions(); std::optional<bool> getAutoScale() const; // @note Use hasExplicitMaxScale to know if the default scale has been overwritten float getMaxScale() const; // @note Use hasExplicitMinScale to know if the default scale has been overwritten float getMinScale() const; bool hasExplicitMaxScale() const; bool hasExplicitMinScale() const; float getRelativeScale() const; std::optional<float> getLength() const; std::optional<float> getPrevGap() const; std::optional<float> getNextGap() const; bool getBreakLine() const; bool getSameLine() const; int getScalePriority() const; std::optional<AxisAlignment> getCrossAxisAlignment() const; /** * Set the maximum scale this node can be if it's contained in an * auto-scaled layout. Default is 1 */ [[deprecated("Use AxisLayoutOptions::setScaleLimits")]] AxisLayoutOptions* setMaxScale(float scale); /** * Set the minimum scale this node can be if it's contained in an * auto-scaled layout. Default is AXISLAYOUT_DEFAULT_MIN_SCALE */ [[deprecated("Use AxisLayoutOptions::setScaleLimits")]] AxisLayoutOptions* setMinScale(float scale); /** * Set the limits to what the node can be scaled to. Passing `std::nullopt` * uses the parent layout's default min / max scales */ AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max); /** * Set the relative scale of this node compared to other nodes if it's * contained in an auto-scaled layout. Default is 1 */ AxisLayoutOptions* setRelativeScale(float scale); /** * Set auto-scaling for this node, overriding the layout's auto-scale * setting. If nullopt, the layout's auto-scale options will be used */ AxisLayoutOptions* setAutoScale(std::optional<bool> enabled); /** * Set an absolute length for this node. If nullopt, the length will be * dynamically calculated based on content size */ AxisLayoutOptions* setLength(std::optional<float> length); /** * Override the default gap in the layout between this node and the * previous one. If nullopt, the default gap of the layout will be used */ AxisLayoutOptions* setPrevGap(std::optional<float> gap); /** * Override the default gap in the layout between this node and the next * one. If nullopt, the default gap of the layout will be used */ AxisLayoutOptions* setNextGap(std::optional<float> gap); /** * 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); /** * If enabled, the node will be forced to be on the same line as the * previous node even if doing this would overflow */ AxisLayoutOptions* setSameLine(bool enable); /** * Set the scale priority of this node. Nodes with higher priority will be * scaled down first before nodes with lower priority when an auto-scaled * layout attempts to fit its contents. Default is * AXISLAYOUT_DEFAULT_PRIORITY * @note For optimal performance, the priorities should all be close to * each other with no gaps */ AxisLayoutOptions* setScalePriority(int priority); /** * Override the cross axis alignment for this node in the layout */ AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment); }; /** * A multi-purpose dynamic layout for arranging nodes along an axis. Can be * used to arrange nodes in a single line, a grid, or a flex layout. The * RowLayout and ColumnLayout classes function as simple thin wrappers over * AxisLayout. The positioning of individual nodes in the layout can be * further controlled using AxisLayoutOptions * @warning Calculating layouts can get increasingly expensive for large * amounts of child nodes being fit into a small space - while this should * never prove a real performance concern as most layouts only have a few * hundred children at the very most, be aware that you probably shouldn't * call CCNode::updateLayout every frame for a menu with thousands of children * @example * auto menu = CCMenu::create(); * // The menu's children will be arranged horizontally, unless they overflow * // the content size width in which case a new line will be inserted and * // aligned to the left. The menu automatically will automatically grow in * // height to fit all the rows * menu->setLayout( * RowLayout::create() * ->setGap(10.f) * ->setGrowCrossAxis(true) * ->setAxisAlignment(AxisAlignment::Start) * ); * menu->setContentSize({ 200.f, 0.f }); * menu->addChild(...); * menu->updateLayout(); */ class GEODE_DLL AxisLayout : public Layout { protected: class Impl; std::unique_ptr<Impl> m_impl; AxisLayout(Axis); public: /** * Create a new AxisLayout. Note that this class is not automatically * managed by default, so you must assign it to a CCNode or manually * manage the memory yourself. See the chainable setters on AxisLayout for * what options you can customize for the layout * @param axis The direction of the layout * @note For convenience, you can use the RowLayout and ColumnLayout * classes, which are just thin wrappers over AxisLayout * @returns Created AxisLayout */ static AxisLayout* create(Axis axis = Axis::Row); virtual ~AxisLayout(); void apply(CCNode* on) override; CCSize getSizeHint(CCNode* on) const override; Axis getAxis() const; AxisAlignment getAxisAlignment() const; AxisAlignment getCrossAxisAlignment() const; AxisAlignment getCrossAxisLineAlignment() const; float getGap() const; bool getAxisReverse() const; bool getCrossAxisReverse() const; bool getAutoScale() const; bool getGrowCrossAxis() const; bool getCrossAxisOverflow() const; std::optional<float> getAutoGrowAxis() const; float getDefaultMinScale() const; float getDefaultMaxScale() const; AxisLayout* setAxis(Axis axis); /** * Sets where to align the target node's children on the main axis (X-axis * for Row, Y-axis for Column) */ AxisLayout* setAxisAlignment(AxisAlignment align); /** * Sets where to align the target node's children on the cross-axis (Y-axis * for Row, X-axis for Column) */ AxisLayout* setCrossAxisAlignment(AxisAlignment align); /** * Sets where to align the target node's children on the cross-axis for * each row (Y-axis for Row, X-axis for Column) */ AxisLayout* setCrossAxisLineAlignment(AxisAlignment align); /** * The spacing between the children of the node this layout applies to. * Measured as the space between their edges, not centres. Does not apply * on the main / cross axis if their alignment is AxisAlignment::Even */ AxisLayout* setGap(float gap); /** * Whether to reverse the direction of the children in this layout or not */ AxisLayout* setAxisReverse(bool reverse); /** * Whether to reverse the direction of the rows on the cross-axis or not */ AxisLayout* setCrossAxisReverse(bool reverse); /** * If enabled, then the layout may scale the target's children if they are * about to overflow. Assumes that all the childrens' intended scale is 1 */ AxisLayout* setAutoScale(bool enable); /** * If true, if the main axis overflows extra nodes will be placed on new * rows/columns on the cross-axis */ AxisLayout* setGrowCrossAxis(bool expand); /** * If true, the cross-axis content size of the target node will be * automatically adjusted to fit the children */ AxisLayout* setCrossAxisOverflow(bool allow); /** * If not `std::nullopt`, then the axis will be automatically extended to * fit all items in a single row whose minimum length is the specified. * Useful for scrollable list layer contents */ AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength); /** * Set the default minimum/maximum scales for nodes in the layout */ AxisLayout* setDefaultScaleLimits(float min, float max); }; /** * Simple layout for arranging nodes in a row (horizontal line) */ class GEODE_DLL RowLayout : public AxisLayout { protected: RowLayout(); public: /** * Create a new RowLayout. See the chainable setters on RowLayout for * what options you can customize for the layout * @returns Created RowLayout */ static RowLayout* create(); }; /** * Simple layout for arranging nodes in a column (vertical line) */ class GEODE_DLL ColumnLayout : public AxisLayout { protected: ColumnLayout(); public: /** * Create a new ColumnLayout. See the chainable setters on RowLayout for * what options you can customize for the layout * @returns Created ColumnLayout */ static ColumnLayout* create(); }; /** * The relative position of a node to its parent in an AnchorLayout */ enum class Anchor { Center, TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left, }; /** * Options for customizing a node's position in an AnchorLayout */ class GEODE_DLL AnchorLayoutOptions : public LayoutOptions { protected: Anchor m_anchor = Anchor::Center; CCPoint m_offset = CCPointZero; public: static AnchorLayoutOptions* create(); Anchor getAnchor() const; CCPoint getOffset() const; AnchorLayoutOptions* setAnchor(Anchor anchor); AnchorLayoutOptions* setOffset(CCPoint const& offset); }; /** * A layout for positioning nodes at specific positions relative to their * parent's content size. See `Anchor` for available anchoring options. Useful * for example for popups, where a popup using `AnchorLayout` can be * automatically resized without needing to manually shuffle nodes around */ class GEODE_DLL AnchorLayout : public Layout { public: static AnchorLayout* create(); void apply(CCNode* on) override; CCSize getSizeHint(CCNode* on) const override; /** * Get a position according to anchoring rules, with the same algorithm as * `AnchorLayout` uses to position its nodes * @param in The node whose content size to use as a reference * @param anchor The anchor position * @param offset Offset from the anchor * @returns A position in `in` for the anchored and offsetted location */ static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset); }; /** * A layout for automatically copying the content size of a node to other nodes. * Basically main use case is for FLAlertLayers (setting the size of the * background and `m_buttonMenu` based on `m_mainLayer`) */ class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout { protected: cocos2d::CCArray* m_targets; public: static CopySizeLayout* create(); virtual ~CopySizeLayout(); /** * Add a target to be automatically resized. Any targets' layouts will * also be updated when this layout is updated */ CopySizeLayout* add(cocos2d::CCNode* target); /** * Remove a target from being automatically resized */ CopySizeLayout* remove(cocos2d::CCNode* target); void apply(cocos2d::CCNode* in) override; cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override; }; #pragma warning(pop) NS_CC_END