add SpacerNode & bring back Dispatch

This commit is contained in:
hjfod 2023-04-04 17:44:49 +03:00
parent c1d4a89f8b
commit b44b5d3cd3
5 changed files with 176 additions and 35 deletions

View file

@ -23,7 +23,7 @@ class CCNode;
*/
class GEODE_DLL Layout : public CCObject {
protected:
CCArray* getNodesToPosition(CCNode* forNode);
CCArray* getNodesToPosition(CCNode* forNode) const;
bool m_ignoreInvisibleChildren = false;
@ -37,6 +37,11 @@ public:
*/
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;
@ -262,6 +267,7 @@ public:
static AxisLayout* create(Axis axis = Axis::Row);
void apply(CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override;
Axis getAxis() const;
AxisAlignment getAxisAlignment() const;

View file

@ -0,0 +1,59 @@
#pragma once
#include "CCNode.h"
NS_CC_BEGIN
#pragma warning(push)
#pragma warning(disable: 4275)
/**
* A node for controlling spacing in Layouts. When a Layout is applied, if
* space is left over, the remaining space is divided among all SpacerNodes in
* the Layout. The space each node gets is the proprotion between its growth
* factor and the sum of all the SpacerNodes' growth factors in the Layout
* @example
* node->addChild(SpacerNode::create(1));
* node->addChild(SpacerNode::create(2));
* node->addChild(SpacerNode::create(1));
* node->updateLayout();
* // Total SpacerNode growth sum is 1 + 2 + 1 = 4
* // So s1 and s3 get 1/4 of the remaining space and s2 gets 2/4
* @note If you want to specify a minimum width for a SpacerNode, add
* AxisLayoutOptions for it and use setLength
*/
class GEODE_DLL SpacerNode : public CCNode {
protected:
size_t m_grow;
bool init(size_t grow);
public:
/**
* Create a new spacer node. When the layout is applied,
* if there is space left over the remaining space is distributed among
* all spacer nodes in proportion to the sum of all the spacers' grow
* factors (akin to CSS flew grow)
* @param grow The grow factor for this node. Default is 1
*/
static SpacerNode* create(size_t grow = 1);
/**
* Set the grow factor for this spacer node. When the layout is applied,
* if there is space left over the remaining space is distributed among
* all spacer nodes in proportion to the sum of all the spacers' grow
* factors (akin to CSS flew grow)
* @param grow The new grow factor for this node. Default is 1
* @note Make sure to call updateLayout on the spacer's parent afterwards
*/
void setGrow(size_t grow);
/**
* Get the grow factor for this spacer node
*/
size_t getGrow() const;
};
#pragma warning(pop)
NS_CC_END

View file

@ -59,6 +59,7 @@ THE SOFTWARE.
// base_nodes
#include "../base_nodes/CCNode.h"
#include "../base_nodes/CCAtlasNode.h"
#include "../base_nodes/SpacerNode.hpp"
// cocoa
#include "../cocoa/CCAffineTransform.h"

View file

@ -9,42 +9,41 @@
namespace geode {
// Mod interoperability
// todo: update to new event system
template <class... Args>
class DispatchEvent : public Event {
protected:
std::string m_id;
std::tuple<Args...> m_args;
// template <typename... Args>
// class DispatchEvent : public Event {
// std::string m_selector;
// std::tuple<Args...> m_args;
public:
DispatchEvent(std::string const& id, Args&&... args)
: m_id(id), m_args(std::make_tuple(args...)) {}
// public:
// DispatchEvent(std::string const& name, Args... args) :
// m_selector(name), m_args(std::make_tuple(args...)) {}
// std::string const& selector() {
// return m_selector;
// }
// };
// template <typename... Args>
// class DispatchHandler : public EventHandler<DispatchEvent<Args...>> {
// std::string m_selector;
// utils::MiniFunction<void(Args...)> m_callback;
// DispatchHandler(std::string const& name, utils::MiniFunction<void(Args...)> callback) :
// m_selector(name), m_callback(callback) {}
// public:
// bool handle(DispatchEvent<Args...>* ev) {
// if (ev->name() == m_selector) {
// std::apply(m_callback, ev->m_args);
// }
// return true;
// }
// static DispatchHandler* create(
// std::string const& name, utils::MiniFunction<void(Args...)> callback
// ) {
// return new DispatchHandler(name, callback);
// }
// };
std::tuple<Args...> getArgs() const {
return m_args;
}
std::string getID() const {
return m_id;
}
};
template <class... Args>
class DispatchFilter : public EventFilter<DispatchEvent<Args...>> {
protected:
std::string m_id;
public:
using Ev = DispatchEvent<Args...>;
using Callback = ListenerResult(Args...);
ListenerResult handle(utils::MiniFunction<Callback> fn, Ev* event) {
if (event->getID() == m_id) {
return std::apply(fn, event->getArgs());
}
return ListenerResult::Propagate;
}
DispatchFilter(std::string const& id) : m_id(id) {}
};
}

View file

@ -50,7 +50,7 @@ bool CCNode::hasAncestor(CCNode* ancestor) {
return false;
}
CCArray* Layout::getNodesToPosition(CCNode* on) {
CCArray* Layout::getNodesToPosition(CCNode* on) const {
auto arr = CCArray::create();
for (auto child : CCArrayExt<CCNode>(on->getChildren())) {
if (!m_ignoreInvisibleChildren || child->isVisible()) {
@ -176,6 +176,32 @@ struct AxisLayout::Row : public CCObject {
{
this->autorelease();
}
void accountSpacers(Axis axis, float availableLength) {
std::vector<SpacerNode*> spacers;
for (auto& node : CCArrayExt<CCNode>(nodes)) {
if (auto spacer = typeinfo_cast<SpacerNode*>(node)) {
spacers.push_back(spacer);
}
}
if (spacers.size()) {
auto unusedSpace = availableLength - this->axisLength;
size_t sum = 0;
for (auto& spacer : spacers) {
sum += spacer->getGrow();
}
for (auto& spacer : spacers) {
auto size = unusedSpace * spacer->getGrow() / static_cast<float>(sum);
if (axis == Axis::Row) {
spacer->setContentSize({ size, this->crossLength });
}
else {
spacer->setContentSize({ this->crossLength, size });
}
}
this->axisLength = availableLength;
}
}
};
struct AxisPosition {
@ -195,6 +221,9 @@ static AxisPosition nodeAxis(CCNode* node, Axis axis, float scale) {
if (auto toggle = typeinfo_cast<CCMenuItemToggler*>(node)) {
scaledSize = toggle->m_offButton->getScaledContentSize();
}
if (auto spacer = typeinfo_cast<SpacerNode*>(node)) {
scaledSize = CCSizeZero;
}
auto anchor = node->getAnchorPoint();
if (axis == Axis::Row) {
return AxisPosition {
@ -611,6 +640,8 @@ void AxisLayout::tryFitLayout(
float rowEvenSpace = available.crossLength / rows->count();
for (auto row : CCArrayExt<Row*>(rows)) {
row->accountSpacers(m_axis, available.axisLength);
if (m_crossAlignment == AxisAlignment::Even) {
rowCrossPos -= rowEvenSpace / 2 + row->crossLength / 2;
}
@ -756,6 +787,24 @@ void AxisLayout::apply(CCNode* on) {
);
}
CCSize AxisLayout::getSizeHint(CCNode* on) const {
// Ideal is single row / column with no scaling
auto nodes = getNodesToPosition(on);
float length = 0.f;
float cross = 0.f;
for (auto& node : CCArrayExt<CCNode*>(nodes)) {
auto axis = nodeAxis(node, m_axis, 1.f);
length += axis.axisLength;
cross += axis.crossLength;
}
if (m_axis == Axis::Row) {
return { length, cross };
}
else {
return { cross, length };
}
}
AxisLayout::AxisLayout(Axis axis) : m_axis(axis) {}
Axis AxisLayout::getAxis() const {
@ -969,3 +1018,30 @@ AxisLayoutOptions* AxisLayoutOptions::setScalePriority(int priority) {
m_scalePriority = priority;
return this;
}
bool SpacerNode::init(size_t grow) {
if (!CCNode::init())
return false;
m_grow = grow;
return true;
}
SpacerNode* SpacerNode::create(size_t grow) {
auto ret = new SpacerNode;
if (ret && ret->init(grow)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
void SpacerNode::setGrow(size_t grow) {
m_grow = grow;
}
size_t SpacerNode::getGrow() const {
return m_grow;
}