basic and dirty yoga layouts impl

This commit is contained in:
BlackMATov
2020-02-11 09:25:53 +07:00
parent d9b1f173a2
commit 5d5d4ecb92
3 changed files with 178 additions and 87 deletions

View File

@@ -130,8 +130,12 @@ namespace sol
namespace e2d::ecsex
{
template < typename T, typename... Opts >
void remove_all_components(ecs::registry& owner, Opts&&... opts) {
template < typename T, typename Disposer, typename... Opts >
void remove_all_components_with_disposer(
ecs::registry& owner,
Disposer&& disposer,
Opts&&... opts)
{
static thread_local vector<ecs::entity> to_remove_components;
E2D_DEFER([](){ to_remove_components.clear(); });
@@ -140,9 +144,18 @@ namespace e2d::ecsex
}, std::forward<Opts>(opts)...);
for ( ecs::entity& e : to_remove_components ) {
std::invoke(disposer, e, e.get_component<T>());
e.remove_component<T>();
}
}
template < typename T, typename... Opts >
void remove_all_components(ecs::registry& owner, Opts&&... opts) {
remove_all_components_with_disposer<T>(
owner,
null_disposer(),
std::forward<Opts>(opts)...);
}
}
namespace e2d::ecsex

View File

@@ -154,6 +154,16 @@ namespace e2d
const char* component_inspector<layout>::title = ICON_FA_BARS " layout";
void component_inspector<layout>::operator()(gcomponent<layout>& c) const {
if ( bool dirty = c.owner().component<layout::dirty>().exists();
ImGui::Checkbox("dirty", &dirty) )
{
if ( dirty ) {
layouts::mark_dirty(c);
} else {
layouts::unmark_dirty(c);
}
}
if ( layout::modes mode = c->mode();
imgui_utils::show_enum_combo_box("mode", &mode) )
{

View File

@@ -10,147 +10,214 @@
#include <enduro2d/high/components/disabled.hpp>
#include <enduro2d/high/components/layout.hpp>
#include <3rdparty/yoga/Yoga.h>
namespace
{
using namespace e2d;
bool is_layout_item_enabled(const const_gcomponent<layout_item>& item) noexcept {
const auto filter = !ecs::exists_any<
disabled<actor>,
disabled<layout_item>
>() && ecs::exists_all<
actor,
layout_item
>();
struct yogo_node final {
using node_ptr = std::shared_ptr<YGNode>;
node_ptr as_item{YGNodeNew(), YGNodeFree};
node_ptr as_root{YGNodeNew(), YGNodeFree};
};
}
return item
|| filter(item.owner().raw_entity())
|| item.owner().component<actor>()->node();
}
namespace
{
using namespace e2d;
void update_layout_items(const layout& layout, const const_node_iptr& layout_node) {
static thread_local vector<gcomponent<layout_item>> items;
E2D_DEFER([](){ items.clear(); });
void update_yogo_node(const layout& l, const actor& a, const yogo_node& yn) {
switch ( l.mode() ) {
case layout::modes::horizontal:
YGNodeStyleSetFlexDirection(yn.as_root.get(), YGFlexDirectionRow);
nodes::extract_components_from_children<layout_item>(
layout_node,
std::back_inserter(items));
//
// layout size
//
v2f layout_size = v2f::zero();
for ( std::size_t i = 0; i < items.size(); ++i ) {
const gcomponent<layout_item>& item = items[i];
if ( !is_layout_item_enabled(item) ) {
continue;
}
layout_size += item->size();
if ( i > 0u ) {
layout_size += layout.spacing();
}
}
//
// cursor offsets
//
v2f item_cursor = v2f::zero();
v2f size_offset_mul = v2f::zero();
v2f cursor_offset_mul = v2f::zero();
if ( layout.mode() == layout::modes::horizontal ) {
cursor_offset_mul = v2f::unit_x();
switch ( layout.halign() ) {
switch ( l.halign() ) {
case layout::haligns::left:
item_cursor = 0.0f * layout_size.x * cursor_offset_mul;
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifyFlexStart);
break;
case layout::haligns::center:
item_cursor = -0.5f * layout_size.x * cursor_offset_mul;
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifyCenter);
break;
case layout::haligns::right:
item_cursor = -1.0f * layout_size.x * cursor_offset_mul;
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifyFlexEnd);
break;
case layout::haligns::space_around:
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifySpaceAround);
break;
case layout::haligns::space_between:
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifySpaceBetween);
break;
default:
E2D_ASSERT_MSG(false, "unexpected layout halign");
break;
}
switch ( layout.valign() ) {
switch ( l.valign() ) {
case layout::valigns::top:
size_offset_mul = -1.0f * v2f::unit_y();
case layout::valigns::space_between:
YGNodeStyleSetAlignItems(yn.as_root.get(), YGAlignFlexEnd);
break;
case layout::valigns::center:
size_offset_mul = -0.5f * v2f::unit_y();
case layout::valigns::space_around:
YGNodeStyleSetAlignItems(yn.as_root.get(), YGAlignCenter);
break;
case layout::valigns::bottom:
size_offset_mul = 0.0f * v2f::unit_y();
YGNodeStyleSetAlignItems(yn.as_root.get(), YGAlignFlexStart);
break;
default:
E2D_ASSERT_MSG(false, "unexpected layout valign");
break;
}
} else if ( layout.mode() == layout::modes::vertical ) {
cursor_offset_mul = v2f::unit_y();
switch ( layout.valign() ) {
break;
case layout::modes::vertical:
YGNodeStyleSetFlexDirection(yn.as_root.get(), YGFlexDirectionColumn);
switch ( l.valign() ) {
case layout::valigns::top:
item_cursor = -1.0f * layout_size.y * cursor_offset_mul;
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifyFlexEnd);
break;
case layout::valigns::center:
item_cursor = -0.5f * layout_size.y * cursor_offset_mul;
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifyCenter);
break;
case layout::valigns::bottom:
item_cursor = 0.0f * layout_size.y * cursor_offset_mul;
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifyFlexStart);
break;
case layout::valigns::space_around:
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifySpaceAround);
break;
case layout::valigns::space_between:
YGNodeStyleSetJustifyContent(yn.as_root.get(), YGJustifySpaceBetween);
break;
default:
E2D_ASSERT_MSG(false, "unexpected layout valign");
break;
}
switch ( layout.halign() ) {
switch ( l.halign() ) {
case layout::haligns::left:
size_offset_mul = 0.0f * v2f::unit_x();
case layout::haligns::space_between:
YGNodeStyleSetAlignItems(yn.as_root.get(), YGAlignFlexStart);
break;
case layout::haligns::center:
size_offset_mul = -0.5f * v2f::unit_x();
case layout::haligns::space_around:
YGNodeStyleSetAlignItems(yn.as_root.get(), YGAlignCenter);
break;
case layout::haligns::right:
size_offset_mul = -1.0f * v2f::unit_x();
YGNodeStyleSetAlignItems(yn.as_root.get(), YGAlignFlexEnd);
break;
default:
E2D_ASSERT_MSG(false, "unexpected layout halign");
break;
}
} else {
break;
default:
E2D_ASSERT_MSG(false, "unexpected layout mode");
break;
}
//
// placing
//
for ( std::size_t i = 0; i < items.size(); ++i ) {
const gcomponent<layout_item>& item = items[i];
if ( !is_layout_item_enabled(item) ) {
continue;
}
const node_iptr& item_node = item.owner().component<actor>()->node();
item_node->translation(item_cursor + item->size() * size_offset_mul);
item_cursor += (item->size() + layout.spacing()) * cursor_offset_mul;
}
}
void update_dirty_layouts(ecs::registry& owner) {
owner.for_joined_components<layout::dirty, layout, actor>([](
const ecs::const_entity&,
const layout::dirty&,
const layout& layout,
const actor& layout_actor)
{
update_layout_items(layout, layout_actor.node());
YGNodeStyleSetWidth(yn.as_root.get(), l.size().x);
YGNodeStyleSetHeight(yn.as_root.get(), l.size().y);
YGNodeStyleSetPadding(yn.as_root.get(), YGEdgeHorizontal, l.padding().x);
YGNodeStyleSetPadding(yn.as_root.get(), YGEdgeVertical, l.padding().y);
}
{
const v2f& scale = a.node()
? a.node()->scale()
: v2f::unit();
YGNodeStyleSetWidth(yn.as_item.get(), l.size().x * scale.x);
YGNodeStyleSetHeight(yn.as_item.get(), l.size().y * scale.y);
YGNodeStyleSetMargin(yn.as_item.get(), YGEdgeHorizontal, l.margin().x * scale.x);
YGNodeStyleSetMargin(yn.as_item.get(), YGEdgeVertical, l.margin().y * scale.y);
}
}
}
namespace
{
using namespace e2d;
void process_yogo_nodes(ecs::registry& owner) {
ecsex::remove_all_components<yogo_node>(
owner,
!ecs::exists_all<
actor,
layout>() ||
ecs::exists_any<
disabled<actor>,
disabled<layout>>());
owner.for_joined_components<layout, actor>([](
ecs::entity e,
const layout& l,
const actor& a)
{
e.ensure_component<yogo_node>();
}, !ecs::exists_any<
disabled<actor>,
disabled<layout>>());
}
void process_dirty_layouts(ecs::registry& owner) {
owner.for_joined_components<layout::dirty, layout, actor, yogo_node>([](
const ecs::const_entity&,
const layout::dirty&,
const layout& l,
const actor& a,
const yogo_node& yn)
{
update_yogo_node(l, a, yn);
});
owner.for_joined_components<layout::dirty, yogo_node, actor>([](
const ecs::const_entity&,
const layout::dirty&,
const yogo_node& root_yn,
const actor& root_a)
{
static thread_local vector<gcomponent<layout>> items;
E2D_DEFER([](){ items.clear(); });
nodes::extract_components_from_children<layout>(
root_a.node(),
std::back_inserter(items));
E2D_DEFER([&root_yn](){
YGNodeRemoveAllChildren(root_yn.as_root.get());
});
for ( const auto& item : items ) {
if ( const auto& item_yn = item.owner().component<yogo_node>() ) {
YGNodeInsertChild(
root_yn.as_root.get(),
item_yn->as_item.get(),
YGNodeGetChildCount(root_yn.as_root.get()));
}
}
YGNodeCalculateLayout(
root_yn.as_root.get(),
YGUndefined,
YGUndefined,
YGDirectionLTR);
for ( const auto& item : items ) {
auto& item_a = item.owner().component<actor>();
const auto& item_yn = item.owner().component<yogo_node>();
if ( item_a && item_a->node() && item_yn && item_yn->as_item ) {
item_a->node()->translation(v2f(
YGNodeLayoutGetLeft(item_yn->as_item.get()),
YGNodeLayoutGetTop(item_yn->as_item.get())));
}
}
});
owner.remove_all_components<layout::dirty>();
}
}
@@ -167,7 +234,8 @@ namespace e2d
~internal_state() noexcept = default;
void process_update(ecs::registry& owner) {
update_dirty_layouts(owner);
process_yogo_nodes(owner);
process_dirty_layouts(owner);
}
};